具有相同标识符的不同类型变量链接在一起

时间:2019-02-25 04:13:46

标签: c gcc

a.h

foreach ($arr as $date => $companies){
    echo  ' <div class="Date">Bookings for '.$date.'</div>';
    foreach ($companies as $company=> $students){
        echo '<div class="Company">Bookings for '.$company.'</div>';
        foreach ($students as $student){
            echo '<div class="Student">'.$student.'</div>';
        }
    }
}

交流

void addr(void);

b.c

#include <stdio.h>

int x;

void addr(void) {
    printf("a:x=%p\n", &x);
}

为什么编译器或链接器不会抱怨两个具有不同类型和相同标识符( x )的extern声明,而它们却默默地链接在一起?

环境:

#include <stdio.h>
#include "a.h"

char x;

int main(void) {
    addr();                 /* a:x=0x601044 */
    printf("b:x=%p\n", &x); /* b:x=0x601044 */

    return 0;
}

2 个答案:

答案 0 :(得分:6)

int x;中的声明a.cchar x;中的b.c只是标识符x的临时定义。

C11标准草案N1570指出:

  

6.9.2外部对象定义
  ...
  2声明对象的标识符,该对象的文件范围为,没有初始化程序,并且没有存储类说明符或具有存储类说明符为静态,构成了一个临时定义 >。

如果您同时在两个文件中都初始化了x(类似于int x = 2;中的a.cchar x = '1';中的b.c,它们将成为“完整”定义,并且那么您将在链接器中遇到多个定义错误。

类似的东西:

Error   LNK1169 one or more multiply defined symbols found  
Error   LNK2005 x already defined in a.obj  

答案 1 :(得分:4)

简介

C标准没有定义两次定义具有外部链接的标识符的行为。通常将某些行为定义为对C的扩展,尤其是在Unix系统上。但是,此扩展依赖于具有兼容类型的定义。通常没有定义int x;char x;的结果。

讨论

两次定义带有外部链接的标识符违反了C标准中的约束,在C 2018 6.9 5中(加粗):

  

如果在表达式中使用了用外部链接声明的标识符(不是作为sizeof_Alignof运算符的操作数的一部分,其结果是整数常量),则在整个程序中的某个位置该标识符应完全是一个外部定义;否则,最多只能有一个。

在您的程序中,x用于表达式&x,因此上述约束适用:x必须有一个外部定义。当违反约束时,根据C 2018 4 2,C标准不会定义结果行为。

为什么int x;char x;的行为不同于int x = 0;char x = 0;?有人可能认为它们应该是相同的,因为前者是暂定的定义(因为它们没有存储类说明符或初始化程序),并且C 2018 6.9.2 2说:

  

如果翻译单元包含一个或多个标识符的临时定义,并且翻译单元不包含该标识符的外部定义,则该行为就好像该翻译单元包含该标识符的文件范围声明,并且从转换单元末尾开始的复合类型,其初始值设定项等于0。

有两个原因。第一个是关于违反约束的规则,该约束导致C标准未定义的行为是覆盖规则;它比有关暂定定义的规则优先。

第二个是,尽管C标准没有定义行为,但是其他文档也可以定义它。正如C 2018 J.5.11(该信息部分而非标准的规范性部分)所指出的那样,对C语言的常见扩展是允许多个外部定义。通常,定义的类型应该一致,并且只能初始化一种。

例如,Systems V Application Binary Interface 描述了在混合有 strong weak 定义或混合有 common 和非公共定义的情况下如何协调多个定义。编译器通过生成一个目标文件来与C的这种扩展配合使用,该目标文件根据标识符具有常规定义还是只是临时定义来对标识符进行不同的标记。例如,使用Apple LLVM 10.0.0和clang-1000.11.45.5 for x86_64编译包含char x;的文件会生成标记为{common section}的符号x,但是编译包含int x = 0;的文件产生标记为x的常规部分。 (将nm命令应用于编译器生成的目标文件时,这些部分分别显示CS。)

摘要

结果是:

  • 两次定义x并不是C标准所定义的。
  • 编译器和链接器扩展了C标准,以允许x的多个临时定义以及最多一个常规定义。
  • 尽管有扩展名,但在一个位置用x定义int在另一位置用char定义'use strict'; var tasks = [ { "title": "home", "color": "blue", }, { "title": "city", "color": "green", }, 的行为是不正确的,但链接器无法对其进行诊断。