37

考虑由两个文件组成的 C 程序,

f1.c:

int x;

f2.c:

int x=2;

我对 C99 标准第 6.9.2 段的解读是,这个程序应该被拒绝。在我对 6.9.2 的解释中,变量x在 中暂定定义f1.c,但这个暂定定义在翻译单元的末尾变成了实际定义,因此(在我看来)应该表现得好像f1.c包含定义一样int x=0;

对于我能够尝试的所有编译器(以及重要的链接器),这不是发生的事情。我尝试的所有编译平台都链接上述两个文件,两个文件中的值x都是2。

我怀疑这是偶然发生的,或者只是作为标准要求之外提供的“简单”功能。如果您考虑一下,这意味着链接器对那些没有初始化程序的全局变量有特殊支持,而不是那些显式初始化为零的变量。有人告诉我,无论如何编译 Fortran 都可能需要链接器功能。这将是一个合理的解释。

对此有什么想法吗?标准的其他解释?哪些平台的文件名f1.cf2.c拒绝链接在一起?

注意:这很重要,因为问题发生在静态分析的上下文中。如果这两个文件可能拒绝在某个平台上链接,分析器应该抱怨,但如果每个编译平台都接受它,那么没有理由警告它。

4

3 回答 3

31

另请参阅什么是 C 中的外部变量。这在信息性附录 J 中的 C 标准中作为通用扩展被提及:

J.5.11 多个外部定义

一个对象的标识符可能有多个外部定义,无论是否显式使用关键字 extern;如果定义不一致,或者不止一个被初始化,则行为未定义(6.9.2)。

警告

正如@litb 在这里指出的那样,并且正如我对交叉引用问题的回答中所述,对全局变量使用多个定义会导致未定义的行为,这是标准的说法“任何事情都可能发生”。可能发生的事情之一是程序的行为符合您的预期;J.5.11 大约说,“你可能比你应得的更幸运”。但是,一个依赖于 extern 变量的多个定义的程序——无论有没有显式的“extern”关键字——并不是一个严格遵守的程序,也不能保证在任何地方都能工作。等效地:它包含一个可能会或可能不会显示自己的错误。

于 2009-09-29T05:54:55.657 回答
11

标准中有一种称为“通用扩展”的东西,只要变量只初始化一次,就可以多次定义变量。见http://c-faq.com/decl/decldef.html

链接页面说这与 Unix 平台有关——我猜 c99 与 c89 相同——尽管它可能已被更多编译器采用以形成某种事实上的标准。有趣的。

于 2009-09-29T05:36:11.767 回答
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
于 2009-09-29T07:25:19.310 回答