全局变量实现

发布于 2024-08-23 01:47:13 字数 1157 浏览 7 评论 0原文

当我编写以下程序时:

文件 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 的问题!

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

$ 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

When I write the following program:

file 1:

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

file 2:

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

file 3:

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

        return 0;
}

output:

$ ./a.out
0x804a01c
0x804a01c

Here is my question:

  • Why are the linker implementing "int global" and "char global" as the same global variable:
  • How come the compiler does not complain (not the smallest warning with -Wall -Wextra -ansi ...)
  • How are the size of the global variable managed (the size of int and char are different)

PS: The second question is architecture/compiler related, so lets take the gcc or Visual C++ (for C) with the int size as 32 bits

EDIT: THIS IS NOT A QUESTION FOR C++ BUT for C!

I use gcc version 4.4.1 and on Ubuntu 9.10, Here is the compilation console output:

$ 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

走过海棠暮 2024-08-30 01:47:13

gcc 不报告任何错误/警告。但 g++ 确实如此。

编辑:

看起来C允许暂定定义< /strong> 为变量。

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

将 file2 更改为:

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

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

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

int global = 1; // in file1

char global = 1; // in file2

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

gcc does not report any error/warnings. But g++ does.

EDIT:

Looks like C allows tentative definitions for a variable.

In your case both the global definitions are tentative and in that case the first one seen by the linker is chosen.

Change your file2 to:

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

Now if you compile like before, the tentative def in file1 will be ignored.

Make both the def explicit by:

int global = 1; // in file1

char global = 1; // in file2

now neither can be ignored and we get the multiple def error.

2024-08-30 01:47:13

这与 C 中所谓的“暂定定义”有关。首先,如果您在 file1 和 file2 中都分配给 global,则 C 中会出现错误。这是因为 global< /code> 不再是在 file1 和 file2 中临时定义的,它是真正定义的。

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

具有文件范围的对象的标识符声明没有初始化程序,并且没有存储类说明符或具有存储类说明符static,构成暂定定义< /em>.如果翻译单元包含标识符的一个或多个临时定义,并且翻译单元不包含该标识符的外部定义,则行为就像翻译单元包含该标识符的文件范围声明一样,复合类型为翻译单元末尾的位置,初始值设定项等于 0。


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

关于“复合类型”:

对于在先前的作用域中声明的具有内部或外部链接的标识符
如果先前的声明指定了内部或内部,则该标识符的声明是可见的
外部链接,后面声明时标识符的类型成为复合类型
类型。

有关暂定定义的更多信息,请参阅 这个问题及其答案

对于您的情况来说,它应该是未定义的行为,因为 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

来自 手册

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

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

This has to do with something called "tentative definition" in C. First, if you assign to global in both file1 and file2, you will get an error in C. This is because global is not tentatively defined in file1 and file2 anymore, it is really defined.

From the C standard (emphasis mine):

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

For your case, "translation unit" (basically) each source file.

About "composite types":

For an identifier with internal or external linkage declared in a scope in which a prior
declaration of that identifier is visible, if the prior declaration specifies internal or
external linkage, the type of the identifier at the later declaration becomes the composite
type.

For more on tentative definitions, see this question and its answers.

It seems like for your case, it should be undefined behavior because global is defined at the end of the translation units, so you get two definitions of global, and what's worse, they are different. Looks like the linker by default doesn't complain about this though.

GNU ld has an option called --warn-common, which warns you for multiple tentative definitions (common symbol is linker's name for tentatively defined variables):

$ 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

From the manual:

If there are only (one or more) common symbols for a variable, it goes in the uninitialized data area of the output file. The linker merges multiple common symbols for the same variable into a single symbol. If they are of different sizes, it picks the largest size. The linker turns a common symbol into a declaration, if there is a definition of the same variable.

The --warn-common option can produce five kinds of warnings. Each warning consists of a pair of lines: the first describes the symbol just encountered, and the second describes the previous symbol encountered with the same name. One or both of the two symbols will be a common symbol.

柳若烟 2024-08-30 01:47:13

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

The linker allows for having duplicate external data like this (although I'm surprised that the different types don't cause a problem). Which one you get depends upon the order of your object files on your link command line.

半枫 2024-08-30 01:47:13

你使用的是哪个编译器。什么是平台?使用 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

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

Which compiler are you using. What is the platform? With g++ I get

/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, in C++ the variables in different translation units much have the exactly same declaration to work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文