【问题标题】:Global variable implementation全局变量实现
【发布时间】:2023-04-07 12:00:02
【问题描述】:

当我编写以下程序时:

文件 1:

#include <stdio.h>    
int global;    
void print_global1() {
        printf("%p\n", &global);
}

文件 2:

#include <stdio.h>
char global;    
void print_global2() {
        printf("%p\n", &global);
}

文件 3:

void print_global1();
void print_global2();
int main()
{
        print_global1();
        print_global2();

        return 0;
}

输出:

$ ./a.out
0x804a01c
0x804a01c

这是我的问题:

  • 为什么链接器将“int global”和“char global”实现为同一个全局变量:
  • 编译器怎么不报错(不是-Wall -Wextra -ansi的最小警告...)
  • 全局变量的大小如何管理(int和char的大小不同)

PS:第二个问题是体系结构/编译器相关的,所以让我们以 int 大小为 32 位的 gcc 或 Visual C++(用于 C)为例

编辑:这不是 C++ 的问题,而是 C 的问题!

我使用 gcc 版本 4.4.1 和 Ubuntu 9.10,这是编译控制台输出:

$ ls
global_data1.c  global_data2.c  global_data.c

$ gcc -Wall -Wextra -ansi global_data*.c
$ ./a.out
0x804a01c
0x804a01c
or 
$ gcc -Wall -Wextra -ansi -c global_data*.c
$ gcc -Wall -Wextra -ansi global_data*.o
$ ./a.out
0x804a01c
0x804a01c

【问题讨论】:

    标签: c variables compiler-construction global-variables


    【解决方案1】:

    您使用的是哪个编译器。平台是什么?使用 g++ 我得到 ​​p>

    /tmp/cc8Gnf4h.o:(.bss+0x0): multiple definition of `global'
    /tmp/ccDQHZn2.o:(.bss+0x0): first defined here
    /usr/bin/ld: Warning: size of symbol `global' changed from 4 in a.o to 1 in b.o
    

    AFAIR,在 C++ 中,不同翻译单元中的变量具有完全相同的声明才能工作。

    【讨论】:

      【解决方案2】:

      链接器允许有这样的重复外部数据(尽管我很惊讶不同的类型不会导致问题)。你得到哪一个取决于你的目标文件在你的链接命令行上的顺序。

      【讨论】:

      • +1 有没有一种方法工具可以让我知道变量“全局”的大小?。
      【解决方案3】:

      gcc 不报告任何错误/警告。但是g++ 可以。

      编辑:

      看起来 C 允许 tentative definitions 用于变量。

      在您的情况下,两个全局定义都是暂定的,在这种情况下,选择链接器看到的第一个。

      将你的文件 2 更改为:

      char global = 1; // no more tentative...but explicit.
      

      现在如果你像以前一样编译,file1 中的暂定 def 将被忽略。

      通过以下方式使两个 def 都显式:

      int global = 1; // in file1
      
      char global = 1; // in file2
      

      现在两者都不能被忽略,我们得到了多重定义错误。

      【讨论】:

      • 我不是在做 C++,而是在做 C 程序(这就是为什么我没有在我的答案中加上 C++ 标志)!看起来 C 标准规范允许这种行为(但我不太确定)。我仍然可以使用 C++ 链接器来检查是否有多个定义,但我不确定它是否真的安全...
      • @Phong:你是对的..C std 允许这样做。我已经更新了我的答案。
      • @codaddict:+1,感谢您的快速更新!我现在更好地理解了编译器是如何处理它的。
      • C99 标准的附录 J 中认为它是一个通用扩展,而不是允许暂定定义的 C。另见:stackoverflow.com/questions/1987413/…
      • @Jonathan:暂定定义一直是 ANSI C 标准的一部分。 C89 标准中的第 3.7.2 节描述了它们。请参阅groups.google.com/group/comp.lang.c/msg/47ae65fdb11e7111 以获得非常好的描述。
      【解决方案4】:

      这与C中称为“暂定定义”的东西有关。首先,如果您在file1和file2中都分配给global,您将在C中得到错误。这是因为global没有暂定定义在 file1 和 file2 中,它真的被定义了。

      来自 C 标准(重点是我的):

      具有文件范围的对象的标识符声明没有初始化程序,并且没有存储类说明符或存储类说明符为静态,构成暂定定义。如果翻译单元包含一个或多个标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,则行为与翻译单元包含该标识符的文件范围声明完全相同,复合类型为翻译单元的末尾,初始化器等于 0。

      对于您的情况,“翻译单元”(基本上)每个源文件。

      关于“复合类型”:

      对于在一个范围内声明的具有内部或外部链接的标识符, 如果先前的声明指定 internal 或 外部链接,后面声明的标识符的类型变成复合的 输入。

      有关暂定定义的更多信息,请参阅this question and its answers

      对于您的情况,它似乎应该是未定义的行为,因为global 是在翻译单元的末尾定义的,所以您会得到global 的两个定义,更糟糕的是,它们是不同的。看起来链接器默认情况下不会抱怨这一点。

      GNU ld 有一个名为 --warn-common 的选项,它会警告您有多个暂定定义(常用符号是暂定定义变量的链接器名称):

      $ gcc -Wl,--warn-common file*.c
      /tmp/ccjuPGcq.o: warning: common of `global' overridden by larger common
      /tmp/ccw6nFHi.o: warning: larger common is here
      

      来自manual

      如果一个变量只有(一个或多个)公共符号,它会进入输出文件的未初始化数据区域。链接器将同一变量的多个常用符号合并为一个符号。如果它们的尺寸不同,它会选择最大的尺寸。如果存在相同变量的定义,则链接器会将公共符号转换为声明。

      --warn-common 选项可以产生五种警告。每个警告由两行组成:第一行描述刚刚遇到的符号,第二行描述之前遇到的同名符号。这两个符号中的一个或两个将是一个公共符号。

      【讨论】:

      • @alok: +1 感谢您教我 -Wl,--warn-common gcc 选项。
      猜你喜欢
      • 1970-01-01
      • 2021-11-13
      • 1970-01-01
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多