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 );
}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に合わせて、修正)

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