第35回
変数の通用範囲~内部広域変数と外部広域変数

ソースファイルの「外」にある変数

これまでは、1つの.cソースファイルから1つの実行形式ファイルを生成してきました。その場合、すべての変数はソースファイルの中で宣言されていなければなりません。しかしCでは、ソースファイルの「外」にある変数を参照することもできます。

どこにも宣言されていない変数

リスト1のソースを見てください。変数aはどこにも宣言されていないため、これをコンパイルするとコンパイラが「aが定義されていない」というエラーメッセージを出し、実行形式ファイルは生成されません。

次にリスト2のソースを見てみましょう。リスト1と違うのは、2行目に
extern int a;
という1行があることです。main関数の外にあるので、これは外部変数の宣言です。ただ先頭に"extern"と付いているところが、通常の変数宣言とは異なっています。

このソースファイルはエラーなくコンパイルされ、実行形式ファイルが生成されます。ただし、"Undefined symbol: a_"といった内容のエラーが出力されます。「識別子 a が見つからない」という意味です。

このエラーはコンパイラではなく、リンカによって出力されます。リンカがこのようなエラーを出しても実行形式ファイルは生成され、それは実行できてしまいます。処理系によって実行結果は異なりますが、LSI Cでは
a = 0
のように「なんとなくもっともな」結果が表示されるはずです。もちろん、これは正しい結果ではありません ※1

サンプルのex3501.cをコンパイルした結果のex3501.exeは、外部変数の説明のためにあえて正しくない動作をするプログラムとなっています
リスト1 : 変数aが宣言されていないソース
#include <stdio.h>

int main(void)
{
  if (a == 0) {              /* ソース中にない変数を参照 */
    printf("a = %d¥n", a);
  }
  return(0);
}

リスト2 : 変数aをextern宣言したソース[ex3501.c
#include <stdio.h>
extern int a;

int main(void)
{
  if (a == 0) {              /* ソース中にない変数を参照 */
    printf("a = %d¥n", a);
  }
  return(0);
}

他のファイルの変数を参照する

コンパイラは.cのソースファイルを読み取って中間形式の.objファイルを生成します。この段階でソース中にコンパイルできない記述があればエラーを発生し、.objファイルは生成されません ※2 。逆に言えば、コンパイルが可能なソースであれば、たとえそれが間違った記述であったとしても.objファイルを生成してしまう――ということです。

一方リンカは、コンパイラが生成した.objファイルとライブラリなどを結合する役目を負っており、.objファイル中に『どこか他のファイル(ライブラリや.objファイル)に存在するかもしれない識別子』があれば、それを探して結合しようとします。

リスト2の
extern int a;
とは、int型の変数aが『このソースの外(ほかのオブジェクトやライブラリ)に存在する』ことを示しています。そのためコンパイルの段階では『変数aはこのソースの中では宣言されていないので、リンク時にほかのファイルから探せ』という形のまま.objファイルが生成されるのです。そしてリンクの段階で『どこを探しても変数aは見つからなかった』とわかり、先の"Undefined symbol: a_"というエラーが出力されます。

とりあえずコンパイルできる場合は、エラーではなく警告のメッセージを出力します。警告だけが出力された場合には.objファイルが生成されますが、大抵の場合そこがバグの元になります