代码之家  ›  专栏  ›  技术社区  ›  Phong

全局变量实现

  •  17
  • Phong  · 技术社区  · 14 年前

    当我编写以下程序时:

    文件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 回复  |  直到 14 年前
        1
  •  12
  •   codaddict    14 年前

    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
    

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

        2
  •  5
  •   Community ƒernando Valle    7 年前

    这与c中的“暂定定义”有关。 global 在file1和file2中,您都会在c中得到一个错误。 全球的 不再在file1和file2中临时定义,它是真正定义的。

    来自C标准(强调我的):

    具有文件作用域的对象的标识符声明 没有初始值设定项 ,如果没有存储类说明符或存储类说明符是静态的,则构成 尝试性定义 . 如果转换单元包含标识符的一个或多个暂定定义,而转换单元不包含该标识符的外部定义,则行为与转换单元包含该标识符的文件范围声明完全相同,复合类型为转换单元末尾的,初始值等于0。

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

    关于“复合类型”:

    对于在 如果先前的声明指定内部或 外部链接,后面声明中的标识符类型将成为 类型。

    有关暂定定义的详细信息,请参见 this question and its answers

    对你的案子来说,这应该是一种未定义的行为,因为 全球的 是在翻译单元的末尾定义的,因此可以得到 全球的 ,更糟糕的是,它们是不同的。看起来链接器默认不会对此抱怨。

    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 :

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

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

        3
  •  1
  •   Scott Smith    14 年前

    链接器允许有这样的重复外部数据(尽管我很惊讶不同的类型不会造成问题)。哪一个取决于link命令行中对象文件的顺序。

        4
  •  1
  •   amit kumar    14 年前

    你在用哪个编译器。站台是什么?用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++中,不同翻译单元中的变量具有完全相同的工作声明。