2024/04/17 更新
この記事は、自作OS Advent Calendar 2018の 12/6 の記事として書かれました。
2018 年 12 月現在、C が使われている事例が減っていて、C 固有の事情を知っている必要性に乏しいという事情もありますが、C++ との非互換の仕様で、あまり知られていない仕様の話を書きます。
私が JIS C99 と呼んでいるのは以下の文書です。
JIS X 3010:2003 プログラム言語C/Programming languages -- C 日本規格協会 JSA Webdesk
https://webdesk.jsa.or.jp/books/W11M0090/index/?bunsyo_id=JIS%20X%203010:2003
JIS C99 規格の「6.9.2 外部オブジェクト定義」の 例1. を読むと、C のグローバル変数は、extern の有無に関係なく外部結合になることが理解できます。
例1. int i1 = 1;//定義,外部結合
int i1; //正しい仮定義,前の定義を参照する
extern int i1; //外部結合をもつ前の定義を参照する
C の規格では、int i1 = 1; のように初期化子を持つことが定義であることに留意する必要があると思います。
グローバル変数を int i1; と初期値なしで書くのは、仮定義と呼ばれています。仮定義は、他の翻訳単位に int i1 = 1; があった場合、初期値 1 のグローバル変数となります。
~$ cat test1.c
#include <stdio.h>
#include <stdlib.h>
extern int i1 = 1;
int t2();
int main()
{
printf("value = %d\n", t2());
return EXIT_SUCCESS;
}
~$ cat test2.c
extern int i1;
int t2()
{
return i1;
}
GCC では警告が出ますが、これはバグ登録されていて対処されていないものです。
~$ gcc test1.c test2.c
test1.c:5:12: warning: ‘i’ initialized and declared ‘extern’
extern int i = 1;
^
~$ ./a.out
value = 1
GCC で警告が消せない問題が対処されないのは、C の規格で決められた奇妙な振る舞いに、開発者が納得していないからなのかもしれません。
45977 – "warning: 'i' initialized and declared 'extern'" could use a separate warning flag controlling it
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=45977
Clang では警告を無視するためのオプション -Wno-extern-initializer が指定できます。
~$ clang test1.c test2.c -Wno-extern-initializer
~$ ./a.out
value = 1
以下は、今回使用した環境です。
~$ gcc --version
gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
~$ clang --version
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
以下は、仮定義の解説文です。C++ には無い C 独自仕様となっています。
以下のページの解説だと、C ではなく C++ でのグローバル変数の解説になっていると思います。
C言語のグローバル変数とexternについて - Gobble up pudding
https://fa11enprince.hatenablog.com/entry/2016/01/10/052009
C++ では extern を付けてグローバル変数を宣言すると実体が定義されず、extern 無しのグローバル変数(定義)を用意しないとリンク時にエラーになるという特性があります。