C言語:extern

全ファイル中のどこかに定義してある、という意味です。複数ファイル構成で分割コンパイルする場合に必要で、一般的には共通ヘッダファイルに記述します。

ファイルが一つだけのプログラムでは、あまり使い道がありません。しかし厳密には、ファイルが一つでもライブラリ内の関数や変数を参照する場合にextern宣言が必要です。ライブラリはコンパイル済みCファイルの集合体だからです。

1. 関数のプロトタイプ宣言

関数のプロトタイプ宣言にexternを付ける付けないは、あまり問題になりません。

----------- a.c ----------- int func_A( int n ); /* OK */ extern int func_A( int n ); /* OK */ int func_A( int n ) { } ----------- b.c ----------- int func_A( int n ); /* OK */ extern int func_A( int n ); /* OK */ int func_C( int n ) { /* a.c の func_A を呼んでいる */ func_A( n ); }

externは「全ファイル中のどこかで宣言されている」ですから、 extern int func_A(int n); は、a.c でも、b.c でも同じように宣言できます。

staticではない関数は、ファイル外部から見えると解釈されるので、関数のexternは省略できます。

2. 変数

2.1 正しい使い方

b.c でint a = 2; を定義して、main.cで使っています。

/* main.c */ #include <stdio.h> extern int a; void main() { printf("a=%d", a); }
/* b.c */ int a = 2;
$ gcc -c main.c b.c $ gcc main.o b.o $ ./a.out a=2

現在のgccでは、main.cの変数aは初期値を設定していないので、externをつけなくても、コンパイルリンクを通りました。

/* main.c */ #include <stdio.h> int a; void main() { printf("a=%d", a); }
/* b.c */ int a = 2;
$ gcc -c main.c b.c $ gcc main.o b.o $ ./a.out a=2

2.3 リンクエラー(1)

main.cでも int a = 2; b.c でも int a = 2; と2箇所で初期値を設定してみました。

/* main.c */ #include <stdio.h> int a = 2; void main() { printf("a=%d\n", a); }
/* b.c */ int a = 2;
$ gcc -c main.c b.c $ gcc main.o b.o b.o:(.data+0x0): `a' が重複して定義されています main.o:(.data+0x0): ここで最初に定義されています collect2: error: ld returned 1 exit status

コンパイルは通りましたが、リンクエラーになりました。

2.4 リンクエラー(2)

main.cでは、extern int a; と宣言しましたが、b.cでは変数aを定義していないケースです。

/* main.c */ #include <stdio.h> extern int a; void main() { printf("a=%d\n", a); }
/* b.c */ int x = 2;
$ gcc -c main.c b.c $ gcc main.o b.o main.o: 関数' 内: main.c:(.text+0x6): `a' に対する定義されていない参照です1 exit status

main.c のコンパイルは通ります。main.cの変数aの扱いは「別ファイルにあるはず」と、変数aの参照に関する問題を先送りにします。しかしリンクのとき、全ファイルを見渡しても変数a はどこにもなく、変数aのアドレスを解決できないので、リンクエラーになります。

(2000年頃の記事)
(2029-03-20 gccに合わせて、修正)

タイトルとURLをコピーしました