引数や戻り値のない関数もある
すべての関数に引数や戻り値があるわけではありません。どちらか片方、あるいは両方ともない関数もあります。
たとえば、前回(「繰り返し)while文の学習で、すべてのLEDを消す処理が出てきました。
P12.3 = 0 ; P4.5 = 0 ; P13.0 = 0 ; P2.3 = 0 ; P2.2 = 0;
この処理を関数にすることを考えてみましょう。この場合、処理内容はひとつだけ(LEDを全部消す)ですから、引数は不要ですね。また、処理結果もありません。単にLEDが消えるだけです。よって戻り値もありません。そこで、リスト4のようになります。
void AllLedOff( ) { P12.3 = 0 ; P4.5 = 0 ; P13.0 = 0 ; P2.3 = 0 ; P2.2 = 0; return; }
1行目、main()で以前から気になっていた? voidが出てきました。voidは英語で「なにもない」とか「無効の」という意味です。C言語では、戻り値のない関数は型をvoidという名前にすることで、そのことを明示する決まりになっているのです。
そして、戻り値がないのに最後にreturn文があります。実はこれは書かなくてもOKです。ですが、「ここでこの関数の処理は終わり」ということを明示する目的で、書いておくほうがよいです。return文が書いてないと、処理の書き忘れなのかどうかが判断しにくくなってしまうためです。
処理を関数に分けた例
関数の学習の最後に、前回(「繰り返し)のwhile文での例「スプーン音がしたらLEDのD7を点灯する」を、処理を関数に分けて書いた例を示します。前回のリストと見比べてみてください。
関数AllLedOff()はリスト4のものを使います。
void WaitSound() { while( P3.0 == 1 ){ ; } return; } void Led_D7( int onoff ) { if ( onoff == 1 ){ P12.3 = 1; } else{ P12.3 = 0; } return; } void main( void ) { AllLedOff(); WaitSound(); Led_D7( 1 ); while(1){ ; } }
LEDのD7の点灯は、前々回(「条件分岐」)で学んだ条件文を入れて少し凝った仕様の関数にしてみました。引数onoffが1なら点灯、0なら消灯の動作をします。ぜひ、PM plus→SM+で動作させて確かめてみてください。1行ずつ実行すると、呼び出しから関数へジャンプする様子を観察することができます。
変数について
関数を学んだところで、もう一度変数について追加説明をします。
第6話でも少し触れましたが、C言語では、変数の宣言のしかたによって、その変数がどこまで影響を及ぼせるか(どこから読み書きできるか)を変えることができます。これを変数のスコープといいます。宣言のしかたは、大きく「宣言の場所」と「staticという言葉をつけるかどうか」で決まります。以下に表にまとめておきます。
| 宣言の場所 | "static"を付けるか | 読み書きできる場所 | 値の保持 |
|---|---|---|---|
| 関数内 | 付けない | 関数の中だけ | しない |
| 関数内 | 付ける | 関数の中だけ | する |
| 関数外 | 付けない | すべての関数から | する |
| 関数外 | 付ける | 宣言のあるファイルの中にある関数だけ | する |
スコープ以外に、値の保持という項目があります。これは、変数の値がいつまで有効かを示しています。「え? 変数に値を入れたらずっと入っているんじゃないの?」と驚かれるかもしれません。実は、関数内で宣言したローカル変数の値だけは、保存されないのです。たとえば先ほどの例のtashizan()関数内の変数kotaeは、tashizan()の処理を終えると消えてしまいます。あとでもう一度tashizan()を呼んだ時にkotaeには何が入っているかは不定なのです。これが困る場合は、staticというキーワードを付けることで値を保存することができます。以下に宣言の例を示します。
/* test.c */ int Var1; static int Var2; func1() { static int Lvar1; int Lvar2; } func2() { static int Lvar3; int Lvar4; }
この場合、Var1は関数外で宣言されていて、staticも付いていません。なので、Var1はどこからでも読み書きできます。プログラムが複数のファイルに分かれていても、別のファイルの別の関数からでも読み書きできるわけです。このような変数を「グローバル変数」と呼びます。
Var2は、宣言の前にstaticという言葉がついています。こうすると、グローバル変数よりも少しだけ制限がうまれて、この宣言をしているファイルの中にある関数からだけ読み書きできるようになります。つまり、上記test.cの中のfunc1()やfunc2()の中からは読み書きできますが、他のファイルにある関数からは読み書きできません。このような変数を一般にファイル内「スタティック変数」と呼びます。
Lvar1とLvar2は関数内で宣言されていますから、その関数func1()の中だけで読み書きできます。これを「ローカル変数」と呼びます。Lvar1はstaticという言葉がついています。こうすると、func1()の処理を終えてもLvar1の値は保持されます。次にfunc1()を呼んだとき、以前のLvar1の値を参照することができるわけです。一方Lvar2はfunc1()の処理を終えると消えてしまいます。
func2()のLvar3とLvar4も同様です。
プログラムで一番よく使うのが関数内のローカル変数(staticなし)です。次がファイル内スタティック変数です。プログラミングに不慣れなうちは、グローバル変数を多用しがちです。なにしろ、どこからでも自由に読み書きできて値も保持されるので便利なことこのうえありません。しかし、大規模なプログラムになると、逆にこのことが災いして不具合の原因になってしまいがちです。ある規模を越えると、もはや誰が(どの関数が)どんなタイミングでその変数にアクセスしているか把握しきれなくなるからです。初心者のうちから、「グローバル変数は最後の手段」くらいに意識しておくほうがよいでしょう。複数の関数から読み書きしたい変数は、それらの関数と1つのファイルにまとめて、ファイル内static変数として「よそからはアクセスしない」ようにしておくのが上策です。
関数内のstatic変数は、滅多に使いません。
次回は
以上で5ルールのうち4つが終わりました。次回は文法編の最後で、簡単な約束事をいくつか学びます。ではまた。
