全ファイル中のどこかに定義してある、という意味です。複数ファイル構成で分割コンパイルする場合に必要で、一般的には共通ヘッダファイルに記述します。
ファイルが一つだけのプログラムでは、あまり使い道がありません。しかし厳密には、ファイルが一つでもライブラリ内の関数や変数を参照する場合に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 );
}
Code language: C++ (cpp)
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);
}
Code language: C++ (cpp)
/* b.c */
int a = 2;
Code language: C++ (cpp)
$ 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);
}
Code language: C++ (cpp)
/* b.c */
int a = 2;
Code language: C++ (cpp)
$ 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);
}
Code language: C++ (cpp)
/* b.c */
int a = 2;
Code language: C++ (cpp)
$ 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);
}
Code language: C++ (cpp)
/* b.c */
int x = 2;
Code language: C++ (cpp)
$ gcc -c main.c b.c
$ gcc main.o b.o
main.o: 関数 `main' 内:
main.c:(.text+0x6): `a' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
Code language: PHP (php)
main.c のコンパイルは通ります。main.cの変数aの扱いは「別ファイルにあるはず」と、変数aの参照に関する問題を先送りにします。しかしリンクのとき、全ファイルを見渡しても変数a はどこにもなく、変数aのアドレスを解決できないので、リンクエラーになります。
(2000年頃の記事)
(2029-03-20 gccに合わせて、修正)