全局变量实现

时间:2010-02-25 04:11:47

标签: c variables compiler-construction global-variables

当我写下面的程序时:

文件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:第二个问题是架构/编译器相关,所以让我们把gcc或Visual C ++(对于C)的int大小为32位

编辑:这不是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

4 个答案:

答案 0 :(得分:12)

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

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

答案 1 :(得分:5)

这与C中称为“暂定定义”的内容有关。首先,如果在file1和file2中分配global,则会在C中出现错误。这是因为global暂时没有在file1和file2中定义,它确实是定义的。

从C标准(强调我的):

  

具有文件范围且没有初始化程序且没有存储类说明符或存储类说明符为static的对象的标识符声明构成暂定定义< / em>的。如果翻译单元包含一个或多个标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,那么行为就像翻译单元包含该标识符的文件范围声明一样,复合类型为翻译单元结尾,初始化程序等于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选项可以产生五种警告。每个警告由一对行组成:第一行描述刚遇到的符号,第二行描述遇到的具有相同名称的符号。两个符号中的一个或两个将是一个共同的符号。

答案 2 :(得分:1)

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

/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 ++中,不同翻译单元中的变量具有完全相同的声明。

答案 3 :(得分:1)

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