2009年07月27日
定義宣言と参照宣言
C の規格に関するちょっとした話です。
今まで、extern が付いていない最上位宣言での変数宣言(グローバル変数)は、全て定義宣言になるのだと思っていたのですが、厳密には異なるそうです。
(1) と (2) は、単に初期値が与えられているかいないかだけの違いで、どちらも定義に見えます。
ところが、厳密には (2) だけが定義宣言で、(1) は仮定義という扱いになるのだそうです。
今まで、extern が付いていない最上位宣言での変数宣言(グローバル変数)は、全て定義宣言になるのだと思っていたのですが、厳密には異なるそうです。
int x; /* (1) */
int y = 0; /* (2) */
extern int x; /* (3) */
(1) と (2) は、単に初期値が与えられているかいないかだけの違いで、どちらも定義に見えます。
ところが、厳密には (2) だけが定義宣言で、(1) は仮定義という扱いになるのだそうです。
つまり、extern が付いていないことと、初期値が与えられていることの 2 つが、定義宣言の条件なのだそうです。
仮定義されたシンボルは、他に定義宣言が存在しなければ、真の定義になるそうです。
というわけで、以下のプログラムは、一見奇妙に見えますが合法ですし、実際にコンパイルして実行できます。
ちなみに C++ では最上位変数の仮定義の仕様は廃止され、真の定義扱いされるそうです。
実際に試してみたところ、上記プログラム (拡張子は .c で) は、gcc では -Wall -ansi -pedantic-errors、cl.exe では /W4 /WX をつけても無警告でコンパイル & 実行できますが、(拡張子を .cpp に変えて) g++/cl.exe (VC++ 2008) では再定義のエラーがたくさん出てコンパイル通りません。
参考文献 : S・P・ハービソン 3 世とG・L・スティール・ジュニアの C リファレンスマニュアル, pp 126-128,131
http://www.amazon.co.jp/dp/4434124234
仮定義されたシンボルは、他に定義宣言が存在しなければ、真の定義になるそうです。
というわけで、以下のプログラムは、一見奇妙に見えますが合法ですし、実際にコンパイルして実行できます。
int x; /* 仮定義はいくつあっても規格合致 */これを例えば、どれか一つの宣言の型を変更したり、初期値を与えたりすると、当然のことながら定義宣言が 2 つ以上になってしまうのでコンパイルが通りません。
int x;
int x;
int x;
extern int x;
extern int x;
extern int x;
int x = 0; /* 定義宣言は一つだけ */
int main()
{
return x;
}
int x;
float x; /* エラー ! */
...
int x = 0;
int x = 1; /* エラー ! */
ちなみに C++ では最上位変数の仮定義の仕様は廃止され、真の定義扱いされるそうです。
実際に試してみたところ、上記プログラム (拡張子は .c で) は、gcc では -Wall -ansi -pedantic-errors、cl.exe では /W4 /WX をつけても無警告でコンパイル & 実行できますが、(拡張子を .cpp に変えて) g++/cl.exe (VC++ 2008) では再定義のエラーがたくさん出てコンパイル通りません。
参考文献 : S・P・ハービソン 3 世とG・L・スティール・ジュニアの C リファレンスマニュアル, pp 126-128,131
http://www.amazon.co.jp/dp/4434124234
トラックバックURL
トラックバック一覧
1. C言語での変数の実体の割り付けられかた [ KMC Staff Blog ] 2009年07月28日 10:01
定義宣言と参照宣言の話に関連して、実際にこのコードをコンパイルして、変数がどう割り当てられるかをみてみましょう。
v.c
>|
int x; /* (1) */
int y = 0; /* (2) */
int z = 1; /* (3) */
extern int x; /* (4) */
||
gcc -S v.c
||
.comm x,4,4
||
.global y...