【问题标题】:Global variable: why "=0" matters?全局变量:为什么“=0”很重要?
【发布时间】:2023-03-22 07:50:02
【问题描述】:

场景:

$ cat t0.c t1.c
/* t0.c */
int i = 12;
/* t1.c */
int i INIT;

int main(void)
{
        return 0;
}

$ gcc t0.c t1.c -DINIT="" -std=c11 -pedantic
<nothing>

$ gcc t0.c t1.c -DINIT="=0" -std=c11 -pedantic
ld: /tmp/ccrTTgwH.o:(.bss+0x0): multiple definition of `i'; /tmp/cckd6R4u.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status

为什么=0 很重要?

更新:

dvl-linux64 $ gcc82 --version
gcc-8.2 (GCC) 8.2.0

试试gcc 11.2.0:

$ gcc t0.c t1.c -DINIT="" -std=c11 -pedantic
ld: /tmp/ccmPBPUT.o:t1.c:(.bss+0x0): multiple definition of `i'; 
$ gcc t0.c t1.c -DINIT="=0" -std=c11 -pedantic
ld: /tmp/ccxw378s.o:t1.c:(.bss+0x0): multiple definition of `i';

预期。

决赛桌:

compiler          -DINIT="" leads to multiple definition of `i'?
gcc 8.2.0         NO
gcc 11.2.0        YES
clang 8.0.1       NO
clang 13.0.0      YES

【问题讨论】:

  • 外部变量只能初始化一次。如果一个文件显示i = 12 而另一个文件显示i = 0,则不能将它们链接在一起。
  • 为什么 -D INIT 被替换为 "=0", "int i;"与“int i = 0;”非常不同
  • 当我尝试这两个命令时,都会导致i 的多个定义。
  • ...确实在这两种情况下都有多个定义,尽管从历史上看,一些编译器已接受这种情况作为扩展,但需要注意各种警告。
  • 一个 extension 在许多传统的 Unix 编译器中默认启用,包括 GCC 直到最近。在最新版本的 GCC 中,您需要启用 -fcommon 才能获得旧的行为。 (在旧的 GCC 中,-fno-common 将关闭该扩展。)

标签: c initialization global-variables c11


【解决方案1】:

就 C 标准而言,两者都是无效的,因为它们构成了标识符的多个外部定义。这实际上是undefined behavior,但是大多数链接器会在这种情况下抛出错误。

对于 t1.c 中的 int i;,这被认为是暂定定义,因为它没有初始化程序。但是,如果同一翻译单元中没有其他定义,则该暂定定义被视为完整定义。

这在C standard 的第 6.9.2p2 节中有描述:

具有文件范围的对象的标识符声明 没有初始化器,也没有存储类说明符或 存储类说明符 static,构成 暂定 定义。如果一个翻译单元包含一个或多个暂定 标识符的定义,并且翻译单元不包含 该标识符的外部定义,则行为正是 好像翻译单元包含该文件的文件范围声明 标识符,与翻译结束时的复合类型 单位,初始化器等于 0。

但是,某些编译器(例如 gcc)将采用暂定定义并将其与其他目标文件中的实际定义“连接”。一旦你在多个目标文件中有一个完整的定义(即使用一个初始化器)(即使初始化器是相同的),它就会产生一个多重定义错误。

-fcommon 选项启用此行为,-fno-common 在 gcc 中禁用它,尽管默认值取决于版本。

【讨论】:

  • 关于“就 C 标准而言,两者都应该是错误的”:没有“应该是”。当一个对象有多个外部定义时,C 标准说该行为是“未定义的”,这意味着该标准对行为没有要求。 “应该是一个错误”将是一个要求。这是一个很好的例子,人们将“未定义的行为”误解为“你不应该做的事情”,而不是“编译器和其他工具可以用自己的定义填充空白”。这正是一些工具所做的;合并是一种已定义的行为。
猜你喜欢
  • 2018-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-12
  • 1970-01-01
  • 2017-07-06
  • 1970-01-01
相关资源
最近更新 更多