今日は、変数の扱いの説明として、変数・関数と引数などを中心に話す。
変数の概念
- 大域変数(global変数)
- 大域変数は、どこからでも触れる変数で、極力使わない….など…
- 局所変数(local変数)
- forループなどのブロック内での変数宣言例など…
//大域変数を使うために起こるトラブル例 int i ; void foo() { for( i = 0 ; i < 2 ; i++ ) printf( "A" ) ; } void main() { // このプログラムは、AAAAと表示されない。 for( i = 0 ; i < 2 ; i++ ) foo() ; }
関数との値の受け渡し
関数と呼び出し側の値の受け渡しということで、値渡し(Call by Value)・ポインタ渡し(Call by Pointer)などの解説。
基本的にC言語では、関数の実引数(real argument)が、仮引数(pseudo argument)に コピーされる値渡しが原則で、関数から複数の値の変化を知りたい場合に、 ポインタと変数のアドレスを使ったポインタ渡しを行うと説明する。
void foo( int *p ) { // ポインタ渡しの例 (*p)++ ; printf( "%d" , (*p) ) ; } void main() { int a = 123 ; foo( &a ) ; foo( &a ) ; }
関数の処理によって値が変化することを副作用と呼び、 副作用は自分の持っているものが想定外に変化するのであれば、 不気味な現象でもあり、極力副作用は小さい方がいい。 だからこそ、変化してほしい値だけをポインタで渡して書き換えてもらう….といった説明を行う。
演算子の前後の()について質問があったので、演算子の優先順位の説明を行う。 ついでだし、右結合・左結合といった話や、趣味の悪い「コンマ演算子」なども 雑学系のネタとして紹介する。
プロトタイプ宣言とプリプロセッサ
この話の中で「なぜ関数実体を読み出し処理より前に書くのか?」との質問がでる。
# 通常私のコーディングは実体を先に書くボトムアップ記述。
基本的にコンパイルは、先頭から命令解析が行われるので、 関数の実体が先にあった方が、関数を呼び出す時に引数や帰り値が何なのか、 分かっていて都合が良いことを説明。 「トップダウン記述」であれば、呼び出し処理を先に書きたいけど、そういうときは プロトタイプ宣言が必要であることを説明する。
これに関連して、プログラム中の#行が、「プリプロセッサ行」であり、 「命令解析前の命令の書き換え処理」が行われると説明し、 "#include <stdio.h>" では、stdio.h というヘッダファイルを読み込み、 そのファイルには、printf(),scanf()のプロトタイプ宣言が書いてあることを説明する。