C99中的暂定定义和链接

Pas*_*uoq 32 c fortran static-analysis compilation c99

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

在f1.c:

int x;
Run Code Online (Sandbox Code Playgroud)

f2.c:

int x=2;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

Jon*_*ler 28

另请参见C中的外部变量.在资料性附件J的C标准中提到这是一个共同的扩展:

J.5.11多个外部定义

对象的标识符可能有多个外部定义,有或没有明确使用关键字extern; 如果定义不一致,或者初始化了多个,则行为未定义(6.9.2).

警告

正如@litb在这里指出的那样,正如我对交叉引用问题的回答所述,对全局变量使用多个定义会导致未定义的行为,这是标准的"任何事情都可能发生"的方式.可能发生的事情之一是程序的行为与您期望的一样; 和J.5.11大致相同,"你可能比你应得的更幸运".但是,一个依赖于外部变量的多个定义的程序 - 有或没有明确的'extern'关键字 - 不是一个严格符合的程序,并不保证在任何地方都可以工作.同等地:它包含一个可能会或可能不会显示自身的错误.

  • 由于这两个变量都在文件范围内并且不是静态的(它们必须像那样存在任何问题),它们都是"外部" - 无论是否使用关键字extern. (3认同)
  • 要清楚它是否被允许:不是它在C中的未定义行为.它就像在做一个`a [10] = 0;`即使`a`是一个`int a [1];`,这是和被允许的作为一个共同的扩展(在我们有灵活的数组成员之前).我认为应该清楚地注意到,除了在某些平台上定义了行为之外,正式执行它是未定义的行为. (2认同)

olo*_*ovb 9

标准中有一个称为"公共扩展"的东西,只要变量只初始化一次,就允许多次定义变量.请访问http://c-faq.com/decl/decldef.html

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


Pas*_*uoq 7

这是为了澄清我对olovb评论的回答:

从"int x;"编译的目标文件的nm输出.在此平台上,符号前面带有'_',即变量x显示为_x.

00000000 T _main
         U _unknown
00000004 C _x
         U dyld_stub_binding_helper
Run Code Online (Sandbox Code Playgroud)

从"int x = 1;"编译的目标文件的nm输出

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper
Run Code Online (Sandbox Code Playgroud)

从"int x = 0;"编译的目标文件的nm输出

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper
Run Code Online (Sandbox Code Playgroud)

从"extern int x;"编译的目标文件的nm输出

00000000 T _main
         U _unknown
         U dyld_stub_binding_helper
Run Code Online (Sandbox Code Playgroud)

编辑:从"extern int x;"编译的目标文件的nm输出 其中x实际用于其中一个函数中

00000000 T _main
         U _unknown
         U _x
         U dyld_stub_binding_helper
Run Code Online (Sandbox Code Playgroud)

  • 如果有人不熟悉nm的输出:D被定义.U未定义.从人类nm - "C"符号是常见的.常用符号是未初始化的数据.链接时,多个常用符号可能会出现相同的名称.如果符号在任何地方定义,则公共符号将被视为未定义的引用. (4认同)