考虑由两个文件组成的C程序,
在f1.c:
int x;
f2.c:
int x=2;
我对the C99 standard第6.9.2段的解读是该程序应该被拒绝。在我对6.9.2的解释中,变量x
暂时在f1.c
中定义,但是这个暂定的定义在翻译单元的末尾变成了一个实际的定义,并且(在我看来),因此应该表现出来好像f1.c
包含定义int x=0;
。
对于所有编译器(以及重要的是,连接器)我能够尝试,这不是发生的事情。我试过的所有编译平台都链接了上面两个文件,两个文件中x
的值都是2。
我怀疑这是偶然发生的,或者只是作为标准要求提供的“简单”功能。如果你考虑一下,这意味着链接器中对那些没有初始化器的全局变量有特殊支持,而不是那些显式初始化为零的全局变量。有人告诉我,无论如何编译Fortran可能都需要链接器功能。这将是一个合理的解释。
对此有何想法?对标准的其他解释?文件f1.c
和f2.c
拒绝链接在一起的平台名称?
注意:这很重要,因为问题出现在静态分析的上下文中。如果两个文件可能拒绝在某个平台上链接,分析器应该抱怨,但如果每个编译平台都接受它,那么就没有理由对此进行警告。
答案 0 :(得分:28)
另见What are extern variables in C。这在C标准中以提供信息的附件J作为共同的扩展名提及:
J.5.11多个外部定义
对象的标识符可能有多个外部定义,无论是否明确使用关键字extern;如果定义不一致,或者初始化了多个,则行为未定义(6.9.2)。
正如@litb在这里指出的那样,正如我对交叉引用问题的回答所述,对全局变量使用多个定义会导致未定义的行为,这是标准的说法“任何事情都可能发生”。可能发生的事情之一是程序的行为与您期望的一样;和J.5.11大致相同,“你可能比你应得的更幸运”。但是一个依赖于外部变量的多个定义的程序 - 有或没有明确的'extern'关键字 - 不是一个严格符合的程序,并不保证在任何地方都可以工作。同等地:它包含一个 bug ,它可能会也可能不会显示出来。
答案 1 :(得分:9)
标准中有一个称为“公共扩展”的东西,只要变量只初始化一次,就允许多次定义变量。见http://c-faq.com/decl/decldef.html
链接页面说这与Unix平台相关 - 我猜c99与c89相同 - 虽然它可能被更多的编译器采用来形成某种事实上的标准。有趣。
答案 2 :(得分:7)
这是为了澄清我对olovb评论的回答:
从“int x;”编译的目标文件的nm输出。在此平台上,符号前面带有'_',即变量x显示为_x。
00000000 T _main
U _unknown
00000004 C _x
U dyld_stub_binding_helper
从“int x = 1;”
编译的目标文件的nm输出00000000 T _main
U _unknown
000000a0 D _x
U dyld_stub_binding_helper
从“int x = 0;”
编译的目标文件的nm输出00000000 T _main
U _unknown
000000a0 D _x
U dyld_stub_binding_helper
从“extern int x;”
编译的目标文件的nm输出00000000 T _main
U _unknown
U dyld_stub_binding_helper
编辑:从“extern int x;”编译的目标文件的nm输出其中x实际用于其中一个函数
00000000 T _main
U _unknown
U _x
U dyld_stub_binding_helper