関数、ポインタ、配列と聞くと、なんだか難しそうだなと思うかもしれません。
慣れないうちは難しいと思うのは当然のことです。プログラミングだけの話ではなく、スポーツや勉強、育児なんかも同じことですよね。誰でも始めからスムーズにできることはないと思んですよね。
だから、いろんな情報を得て吸収しようとするんです。
今回は、関数だけに焦点を絞ってお話ししていこうと思います。
関数とは
プログラムの中で関数とは、ある機能や処理をまとめて名前をつけて、{}で括ったものです。
一度作ってしまえば、使いまわしができることも特徴のひとつです。
こんな感じです。
short func(short a) { short b; b = a * 2; return b; }
上の例では関数名がfunc、引数がshort型1つ、戻り値がshort型で、関数の処理内容は「引数を2倍した値を求めて返す」というものです。
関数には、引数、戻り値を持つもの、引数なしで戻り値を持つもの、引数はあるが、戻り値を持たないもの、引数、戻り値ともに持たないものの4種類あります。
引数、戻り値を持つ関数は、上の例のように算術演算とか戻り値が複数必要だったりする場合に使うことが多いでしょう。
引数なしで戻り値を持つ関数は、別ファイルに記載している変数の内容を参照する場合に使うことが多いでしょう。
引数ありで戻り値を持たない関数は、別ファイルに記載している変数の値を変更する場合に使うことが多いでしょう。
引数、戻り値ともに持たない関数は、重複する処理を一まとめたり、冗長になってしまっている処理をまとめたりする場合に使うことが多いでしょう。
関数を使うメリット・デメリット
プログラムはmain()で始まりますが、この中にすべての処理を書いて完結させてしまっても問題ありません。
しかし、それでは、大きいプログラムを作ったり、複雑なプログラムを作る場合、どうしてもプログラムが見にくくなります。
ここでは、関数を使うメリット、デメリットを挙げていきます。
●関数を使うメリット
- プログラムが見やすくなる。
関数を作っていくことで、プログラムが見やすくなるのは言うまでもありません。
関数を作るサイズは1画面から1画面半くらいに収まるくらいが理想とされていますが、フォントやエディタなどパソコンの環境が異なるので、こだわる必要はないと思います。
しかし、あまり関数の処理が長すぎると、プログラムが見やすくなるというメリットが失われてしまいます。
したがって、長すぎる場合は、さらに小さな関数が作って、1つの関数が長くならないように努力しましょう。 - 同じプログラムを書かなくて済む。
同じような処理がいくつも出てくるような場合、関数にするべきです。
見やすさもそうですが、同じプログラムを何回も書くとミスも生じます。
それに、関数にしておけば、同じプログラムを書かなくても、関数をコールするだけでプログラミングが完了します。 - 修正箇所が少なくて済む。
もし、その関数内に間違いがあった場合、修正が1か所で済みます。
しかし、関数にしておらず、同じ処理をたくさん書いていたら、修正個所が多岐にわたりますよね。そうなると、時間もかかりますし、抜けも出てしまいます。
また、間違いはなくても、機能追加したい場合も同様ですね。
●関数を使うデメリット
- 処理の流れがわかりにくくなる可能性がある。
関数を作っていくので、1つ1つのブロックは小さくなって、見やすくなりますが、プログラムを読んでいったときに、関数があると、その関数へジャンプして見にいかなくてはなりませんので、処理の流れがわかりにくくなる可能性があります。
基本は、出てくる関数を順番に上から書いていくのがよいでしょう。 - 実行時間が長くなる。
関数を記載している場所に実行箇所をジャンプして移るので、その分時間がかかります。大した時間ではありませんが。
引数の数にも影響するので、引数は4つまでが理想です。
しかし、最近のパソコン、マイコンは性能が上がっているので、関数化するしないで、実行時間が影響するようなことはなくなっているようです。 - 間違いがあった場合、使用している全ての箇所に影響が出る。
メリットの逆になりますが、間違いがあると、関数を使用している箇所にすべてに影響してしまいます。同じプログラムを書いていても、手で同じことを書いていた場合は、間違いが1か所だけで済む場合があります。コピペではすべてが間違いになりますが。
関数を使うメリット・デメリットは以上です。
関数を使うデメリットといっても大したデメリットになりませんので、関数をどんどん使っていくことをお勧めします。
関数の作り方
関数を使うことがいいということはわかったが、どうやって関数を作るんだ?
という声が聞こえてきそうですので、関数の作り方を説明します。
関数の見た目は、先ほど例で作った感じです。
手順としては以下です。
- 関数にする処理を決める。
- 関数名を決める。
- 関数の引数の型、個数を決める。
- 関数の戻り値の有無、型を決める。
- 関数の処理を記述する。
ここで、注意してほしいのは、関数を定義する前に、関数を使ってはいけません。コンパイルエラーまたはリンクエラーになります。
関数が出てくる順に記載するというのは、こういう理由もあるのです。
どうしても、関数をファイルの最後に書くのならば、ファイルの先頭のほうに変数と同様に関数を定義しておく必要があります。
あるいは、ヘッダファイル(.h)に記載する方法もあります。ヘッダファイルに記載する場合は、他のファイルでも使用するためですので、他のファイルで使用しないのなら、使うファイル(.c)内で宣言するべきです。
static 関数の戻り値の型 関数名(引数の型 引数名, …);
と記載すればいいです。ただし、作成する関数と同じ名前、戻り値の型、引数の型、個数でなければなりません。
static short func1(short a, short b); /*************************/ /* a + bの値を求める関数 */ /* 引数 a : 足される数 */ /* b : 足す数 */ /* 戻り値 : sum(a + b) */ /*************************/ static short func1(short a, short b) { short sum; /* 足し算の結果を格納する変数宣言 */ sum = a + b; /* a + bの結果をsumに格納 */ return sum; /* sumの値を返す */ }
と記載すれば、2つの数を足した結果を返す関数の完成です。厳密には(a + b)をしたときにshort型の取りうる範囲を超えてしまわないか?
というような判定をsumに入れる前に考慮したほうがいいかもしれません。
ここでは、関数の作り方の話なので、これ以上は深堀しないことにします。
同様に、引き算、掛け算、割り算をする関数を作ることができます。
static short func1(short a, short b); static short func2(short a, short b); static short func3(short a, short b); static short func4(short a, short b); /*************************/ /* a + bの値を求める関数 */ /* 引数 a : 足される数 */ /* b : 足す数 */ /* 戻り値 : sum(a + b) */ /*************************/ static short func1(short a, short b); { short wa; /* 足し算の結果を格納する変数宣言 */ wa = a + b; /* a + bの結果をwaに格納 */ return wa; /* waの値を返す */ } /*************************/ /* a - bの値を求める関数 */ /* 引数 a : 引かれる数 */ /* b : 引く数 */ /* 戻り値 : sa(a - b) */ /*************************/ static short func2(short a, short b); { short sa; /* 引き算の結果を格納する変数宣言 */ sa = a - b; /* a - bの結果をsaに格納 */ return sa; /* saの値を返す */ } /*************************/ /* a * bの値を求める関数 */ /* 引数 a : 掛けられる数 */ /* b : 掛ける数 */ /* 戻り値 : seki(a * b) */ /*************************/ static short func3(short a, short b); { short seki; /* 掛け算の結果を格納する変数宣言 */ seki = a * b; /* a * bの結果をsekiに格納 */ return seki; /* sekiの値を返す */ } /*************************/ /* a / bの値を求める関数 */ /* 引数 a : 割られる数 */ /* b : 割る数 */ /* 戻り値 : shou(a / b) */ /*************************/ static short func4(short a, short b); { short shou; /* 割り算の結果を格納する変数宣言 */ if (b != 0) { shou = a / b; /* a / bの結果をshouに格納 */ } else { shou = 0x7fff; /* 0では割れないので、short型の最大値をshouに格納する */ } return shou; /* shouの値を返す */ }
割り算だけ、0で割ることができませんので、判定文を入れました。他にも、掛け算の関数は戻り値をlongにしたほうがオーバーフローを防げるでしょう。
32767 * 32767をされると、short型では数を表すことができませんから。
関数の使い方
関数は作っただけでは何の機能も発揮しません。実行する箇所から呼んで始めて実行されます。ですから、関数を作ったら、関数を呼ぶ処理を追加することを忘れないようにしましょう。
では、関数の使い方を見ていきましょう。
関数は、先ほど作った、四則演算を使うことにして、これらをmain関数から呼ぶことにします。
main関数はプログラムが実行されるとき、最初にコールされる関数です。
#inclue <stdio.h> static short func1(short a, short b); static short func2(short a, short b); static short func3(short a, short b); static short func4(short a, short b); /*************************/ /* a + bの値を求める関数 */ /* 引数 a : 足される数 */ /* b : 足す数 */ /* 戻り値 : sum(a + b) */ /*************************/ static short func1(short a, short b); { short wa; /* 足し算の結果を格納する変数宣言 */ wa = a + b; /* a + bの結果をwaに格納 */ return wa; /* waの値を返す */ } /*************************/ /* a - bの値を求める関数 */ /* 引数 a : 引かれる数 */ /* b : 引く数 */ /* 戻り値 : sa(a - b) */ /*************************/ static short func2(short a, short b); { short sa; /* 引き算の結果を格納する変数宣言 */ sa = a - b; /* a - bの結果をsaに格納 */ return sa; /* saの値を返す */ } /*************************/ /* a * bの値を求める関数 */ /* 引数 a : 掛けられる数 */ /* b : 掛ける数 */ /* 戻り値 : seki(a * b) */ /*************************/ static short func3(short a, short b); { short seki; /* 掛け算の結果を格納する変数宣言 */ seki = a * b; /* a * bの結果をsekiに格納 */ return seki; /* sekiの値を返す */ } /*************************/ /* a / bの値を求める関数 */ /* 引数 a : 割られる数 */ /* b : 割る数 */ /* 戻り値 : shou(a / b) */ /*************************/ static short func4(short a, short b); { short shou; /* 割り算の結果を格納する変数宣言 */ if (b != 0) { shou = a / b; /* a / bの結果をshouに格納 */ } else { shou = 0x7fff; /* 0では割れないので、short型の最大値をshouに格納する */ } return shou; /* shouの値を返す */ } void main(void) { short val1, val2; short wa, sa, seki, shou; /* 間違えて文字を入力してしまうと暴走します */ /* val1の値をキーボードから入力する */ printf("val1 = "); scanf("%d", &val1); /* val2の値をキーボードから入力する */ printf("val2 = "); scanf("%d", &val2); /* 足し算 */ /* 足し算関数をコールし、引数にval1, val2を渡し、結果をwaへ代入する */ wa = func1(val1, val2); /* 引き算 */ /* 引き算関数をコールし、引数にval1, val2を渡し、結果をsaへ代入する */ sa = func2(val1, val2); /* 掛け算 */ /* 掛け算関数をコールし、引数にval1, val2を渡し、結果をsekiへ代入する */ seki = func3(val1, val2); /* 割り算 */ /* 割算関数をコールし、引数にval1, val2を渡し、結果をshouへ代入する */ shou = func4(val1, val2); /* 足し算結果を出力 */ printf("val1 + val2 = %d", wa); /* 引き算結果を出力 */ printf("val1 - val2 = %d", sa); /* 掛け算結果を出力 */ printf("val1 * val2 = %d", seki); /* 割算結果を出力 */ printf("val1 / val2 = %d", shou); }
どうでしょうか?
使い方は簡単ですね。使いたいときに、関数を呼べばいいのです。
ただし、関数の引数の型、戻り値の型はきちんと合わせておきましょう。
異なった型でも実行可能ですが、その場合は、必ずしも正しい結果にならないということを認識しておきましょう。
関数を使うことは難しいことではないということが、おわかりいただけたと思います。
算術演算での例でしたが、これからいろんなプログラミングをしていくうえで、いろんな関数と出会ったり、作ったりすると思います。
しかし、決して関数を使うことは難しいことはありません。
どんどんと挑戦していただけたらと思います。