C言語ではスコープで空間が分離されるため、そのスコープ内で宣言された変数は、別のスコープからは参照できないという解説を、C言語014 ループ制御で行いました。そのため、関数間の変数の取り回しは引数で渡して戻り値で受け取るというのが基本となります。ですが、その引数だけではデータのやりとりが不十分の事が多々あります。関数間で共通に利用できる変数、それがグローバル変数です。
--
いきなりここに飛んで来ちゃった人は、よろしければ下記からご覧ください。


  • グローバル変数宣言
関数やループのスコープ内で宣言された変数はローカル変数と言います。ローカル変数は宣言されたスコープの終了とともに消滅します。そして、C言語では、スコープ範囲外でも変数を宣言する事が出来ます。これがグローバル変数です。

変数はスコープを閉じると消滅しますが、どのスコープにも所属しないグローバル変数は、プログラムが終了するまで消滅しません。そのため、一切の制限なく、全ての関数から全く同様に読み書きする事が出来ます。

具体的な例を載せます。

int gNum; void SetNumber(void) { gNum = 10; } void PutNumber(void) { printf("Number = %d\n", gNum); } int main(void) { SetNumber(); PutNumber(); return EXIT_SUCCESS; }

一番最初に宣言した gNum がグローバル変数です。SetNumber 関数で 10 の値を設定して、PutNumber 関数でその値を画面に表示しています。スコープで区分されていないグローバル変数だから、全ての関数から自由に使う事が出来ています。


  • グローバル変数の危険性
グローバル変数は C言語における必要悪です。これがないとなかなか大規模プログラムの作成は困難ですが、これのおかげでバグの原因が分かりにくくなる諸悪の根源とも言えます。アセンブラから C言語を学んだ人はポインタでは苦労する事は無かったのですが、このグローバル変数を乱立させてバグを多発させていた経緯があったりします。

そのため、グローバル変数は必要最小限に留める実装を心がけるようにしてください。基本的にはローカル変数で処理する事。それがバグを減らす秘訣となります。まずはこの事を肝に銘じておいてください。

どうして危険なのか、極端な例を書いてみます。

int sum[5][5]; void LoopHeight(int x) { int num = 0; for (int i = 0; i < 5; ++i) { sum[x][i] = num++; } } void PutArray(void) { for (int y = 0; y < 5; ++y) { for (int x = 0; x < 5; ++x) { printf("%2d, ", sum[x][y]); } printf("\n"); } } int main(void) { for (int i = 0; i < 5; ++i) { LoopHeight(i); } PutArray(); return EXIT_SUCCESS; }

サンプル用にわざわざわかりにくい処理系で書いてみました。二次元配列の初期化だけを行う処理としては簡単な内容となっています。実行結果は以下の通りです。
正常な二次元配列初期化状態
さて、処理内ではローカル変数 i でそれぞれの関数でループしていますが、この i をグローバル変数に変更するとどうなるでしょうか。プログラムの構造は全く変えていません。

int sum[5][5]; int i; void LoopHeight(int x) { int num = 0; for (i = 0; i < 5; ++i) { sum[x][i] = num++; } } void PutArray(void) { for (int y = 0; y < 5; ++y) { for (int x = 0; x < 5; ++x) { printf("%2d, ", sum[x][y]); } printf("\n"); } } int main(void) { for (i = 0; i < 5; ++i) { LoopHeight(i); } PutArray(); return EXIT_SUCCESS; }

これでも動きます。
異常な二次元配列初期化状態
…動きますが初期化が不十分になりました。ループ変数 i が共通化されているので、main 関数の i が、LoopHeight で変更されて正しいループが出来なくなっているのです。しかしながら、ソースコードをパッと見ただけで、そのようなバグが出る構造に見えなくなるのが、グローバル変数を使った弊害となります。

この例でもかなり分かりづらいのですが、i がもっとあちこちで使用されているとしたら怖くないですか?個人的にはこれは相当怖いです。バグが出やすく、またそのバグの原因を掴みづらい感覚を共有してもらえれば幸いです。
※ スマートウォッチも値段と性能がこなれてきた感があります。そろそろ身につけてみてはどうでしょうか。