プロコンの競技部門の学生さんたちが、最後の追い込み。 各パーツに分かれて動作確認が取れてきたし、 1つのプログラムに合成する最後の段階になっている様子。 しかしながら、合成した後プログラムが動かないとの相談。 1度目の処理は動くけど、2回目で動かないという状態なので、 不完全な初期化データを使って動かなくなっているのではと想像する。 といっても、そう簡単に間違いが見つかる訳もないけど、 ひとまずコードを見せてもらう。 すると、覚えたての分割コンパイルで、ヘッダファイルの中に、 static int array[…] ; といった記載が見つかる。
このままでは、array[] が、各C言語ファイル毎に、file-scope の別な実体を持つため、 大域変数渡しの副作用が伝わらなかったり、その結果として未初期化が発生したり。
ファイルスコープ
C言語では、静的変数の局所変数を作りたい場合、関数ブロック内で "static"キーワードを指定すればよい。 しかしながら、関数ブロック外で static キーワードをつけると、 分割コンパイルした場合、file-scope を持つようにコンパイルされる。 つまり、各ファイル毎の大域変数は、たとえ同じ名前の大域変数が別ファイルに あったとしても、別な記憶領域を確保してくれる。
(( aaa.c )) int x1 ; // 静的変数・大域変数 static int x2 ; // 静的変数・大域変数・ファイルスコープを持つ void foo() { static int x3 ; // 静的変数で局所変数 : } (( bbb.c )) extern int x1 ; // aaa.c の x1と同じ実体を参照できる。 static int x2 ; // aaa.cのx2とは大域変数だけど別の実体をもつ
分割コンパイルして、各ファイルで共通の変数や関数を定義する場合は、 以下のように行う。 ポイントは、extern によって宣言すると、実体は別のところにある変数となる。
(( common.h )) int foo() ; // プロトタイプ宣言 extern int bar ; // 実体はどこかで確保される変数として宣言 (( aaa.c )) #include "common.h" int bar = 123 ; // 変数barの実体 int foo() { // 関数foo()の実体 } (( bbb.c )) #include "common.h" void main() { bar = 234 ; // aaa.c の大域変数barに代入 printf( "%d" , foo() ) ; // aaa.c の関数foo()を呼び出し }