第34回
変数の通用範囲~自動変数と静的変数/局所変数と広域変数

局所変数と広域変数

自動変数と静的変数──関数内で消滅する変数とプログラムの実行中存在し続ける変数──は、変数が『いつ生成されて、いつ消滅するのか?』を規定するものです。これとは別に、変数には局所変数と広域変数という分類があります。

変数を参照できる範囲

局所変数は「ローカル変数」とも呼ばれ、『一定の処理範囲内だけで参照できる変数』です。広域変数は「グローバル変数」とも呼ばれ、『複数の処理範囲をまたいで参照できる変数』です。

局所変数は、関数の中で宣言された変数です。関数の中で宣言された変数は、自動変数であっても静的変数であっても、宣言された関数内からのみその値を参照でき、他の関数から参照することはできません。

一方広域変数は『関数の外』で宣言され、同じソースファイル中のすべての関数から値を参照できます。

広域変数は関数の外で宣言されるため、一般には宣言と同時に初期化します。また、main関数の中で他の関数を呼び出す前に初期化することもできます。


プログラムで試す

広域変数と局所変数の違いをプログラムで試してみましょう。リスト2では、int型の変数aが広域変数で、main関数より先に(すべての関数の外側で)宣言され、0で初期化されています。

main関数では関数countを10回呼び出しています。count関数の中ではint型の局所変数bを宣言し、0で初期化しています。

その後、先のリスト1と同じく2つの変数の値を表示し、続いて両者の値を1増加しています。

実行すると画面2のようになります。count関数内で宣言されている局所変数bは呼び出されるたびに初期化されるため、常に0となっています。一方、広域変数aはcount関数の外で宣言されていますがその値を参照できるため、count関数で値を表示したりインクリメントしたりできます。

リスト2:グローバル変数とローカル変数の働きを確かめる(ex3402.c)
#include <stdio.h>

int a = 0;        /* 広域変数の宣言と初期化 */
void count(void);		/* 関数の宣言 */

int main(void)
{

  int i;
  /* countを10回呼び出す */
  for (i = 0; i < 10; i++) {
    printf("%d : ", i+1);    /* 呼び出し回数 */
    count();
  }
	return (0);
}

/* 呼び出された回数を表示 */
void count(void)
{
  int b = 0;        /* 局所変数の宣言と初期化 */

  printf("global -- %d / local -- %d\n", a, b);

  a++;       /* 広域変数を1加算 */
  b++;       /* 局所変数を1加算 */
}

画面2:ex3402.exeの実行結果
C:\CLANG\EXE>ex3402
1 : global -- 0 / local -- 0
2 : global -- 1 / local -- 0
3 : global -- 2 / local -- 0
4 : global -- 3 / local -- 0
5 : global -- 4 / local -- 0
6 : global -- 5 / local -- 0
7 : global -- 6 / local -- 0
8 : global -- 7 / local -- 0
9 : global -- 8 / local -- 0
10 : global -- 9 / local -- 0
C:\CLANG\EXE>

このように広域変数は、ソースファイル内の関数から自在に参照できます。しかし、たとえばmain関数の中で次のようにしてcount関数内で宣言された変数bを参照することはできません。
printf("b = %d /", b);

コンパイル時に「'b'という識別子が定義されていない」というエラーが発生します。変数bは、main関数とは別の処理単位である関数count内で宣言された変数だからです。

広域変数の多用はNG!

広域変数はどこからでも参照できるため、関数内で消滅する局所変数より便利な気がします。そのため、プログラムで必要なデータをすべて広域変数としてしまう人もいるようです。

しかし、広域変数は「どの処理(関数)がその値を書き換えたのか」が見通しにくくなるため、プログラムの構造を把握することが困難になり、デバッグや仕様変更の際に苦労します。

Cでは、関数同士のデータのやり取りは引数と戻り値で行い、広域変数の使用は極力控える――というのがセオリーです。


変数には、

  宣言された処理単位(関数)内でのみ存在する自動変数
  処理を終えても存在して値を保持し続ける静的変数

の2種類があります。これは、変数の『通用期間』に関する違いです。

さらに

  宣言された処理単位(関数)内でのみ参照できる局所変数
  処理単位の外からでも参照できる広域変数

の2種類があります。これは、変数の『通用範囲』に関する違いです。