C和C++中的全局变量有什么区别?

sun*_*t07 27 c c++ gcc global-variables

我测试了以下代码:

在文件ac/a.cpp中

int a;
Run Code Online (Sandbox Code Playgroud)

在文件bc/b.cpp中

int a;
int main() { return 0; }
Run Code Online (Sandbox Code Playgroud)

当我使用gcc*.c -o test编译源文件时,它会成功.

但是当我使用g ++*.c -o test编译源文件时,它会失败:

ccIJdJPe.o:b.cpp:(.bss+0x0): multiple definition of 'a'
ccOSsV4n.o:a.cpp:(.bss+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

我真的很困惑.C和C++中的全局变量之间有什么区别吗?

lpa*_*app 30

以下是该标准的相关部分.请参阅标准文本下面的解释:

§6.9.2/ 2外部对象定义

具有没有初始化程序的文件范围且没有存储类说明符或存储类说明符为静态的对象的标识符声明构成暂定定义.如果翻译单元包含一个或多个标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,那么行为就像翻译单元包含该标识符的文件范围声明一样,复合类型为翻译单元的结尾,初始化程序等于0.

ISOC99§6.9/ 5外部定义

外部定义是外部声明,它也是函数(内联定义除外)或对象的定义.如果在表达式中使用通过外部链接声明的标识符(而不是作为sizeof运算符的操作数的一部分,其结果是整数常量),则整个程序中的某个地方应该只有一个标识符的外部定义; 否则,不得超过一个.

使用C版本,'g'全局变量被"合并"为一个,因此在一天结束时只会声明两次.这是可以的,因为不需要extern,或者可能没有退出.因此,这是出于编写旧代码的历史和兼容性原因.这是此传统功能的gcc扩展.

它基本上使gcc为名为'a'的变量分配内存,因此可以有多个声明,但只有一个定义.这就是为什么即使使用gcc,下面的代码也无法正常工作.

这也称为暂定定义.C++没有这样的东西,就在它编译的时候.C++没有暂定声明的概念.

暂定定义是没有存储类说明符且没有初始化程序的任何外部数据声明.如果到达翻译单元的末尾并且没有出现具有标识符的初始化器的定义,则暂定定义变为完整定义.在这种情况下,编译器会为定义的对象保留未初始化的空间.

但请注意,即使使用gcc,以下代码也不会编译,因为这是暂定的定义/声明,并且已分配值:

在文件"ac/a.cpp"

int a = 1;
Run Code Online (Sandbox Code Playgroud)

在文件"bc/b.cpp"

int a = 2;
int main() { return 0; }
Run Code Online (Sandbox Code Playgroud)

让我们通过进一步的例子来讨论这个问题.以下陈述显示了正常定义和暂定定义.注意,静态会使它有点不同,因为那是文件范围,并且不再是外部的.

int i1 = 10;         /* definition, external linkage */
static int i2 = 20;  /* definition, internal linkage */
extern int i3 = 30;  /* definition, external linkage */
int i4;              /* tentative definition, external linkage */
static int i5;       /* tentative definition, internal linkage */

int i1;              /* valid tentative definition */
int i2;              /* not legal, linkage disagreement with previous */
int i3;              /* valid tentative definition */
int i4;              /* valid tentative definition */
int i5;              /* not legal, linkage disagreement with previous */
Run Code Online (Sandbox Code Playgroud)

更多详细信息可以在以下页面中:

http://c0x.coding-guidelines.com/6.9.2.html

有关更多详细信息,请参阅此博客文章:

http://ninjalj.blogspot.co.uk/2011/10/tentative-definitions-in-c.html

  • 我认为答案是误导性的.你引用和讨论暂定的定义,但在这种情况下它们是一个红色的鲱鱼.暂定的定义仅仅是关于在翻译**单元中多个_apparent_定义是合法的**.问题的关键在于当单独的翻译单元中存在多个定义时,C版本似乎链接的原因.就个人而言,我认为你应该更清楚代码是错误的. (8认同)
  • 正如你的引言所说,暂定声明与翻译单元末尾__完整声明完全相同.这意味着每个翻译单元仍然包含"a"的定义,并且程序仍然出错,因为它违反了6.9/5.gcc只接受代码,因为它实现了标准的扩展. (4认同)

CB *_*ley 5

gcc实现了一个遗留功能,其中未初始化的全局变量放在一个公共块中.

尽管在每个翻译单元中,定义是暂定的,但在ISO C中,在翻译单元的末尾,如果暂定定义尚未合并为非暂定定义,则将其"升级"为完整定义.

在标准C中,即使这些定义来自暂定定义,在多于一个翻译单元中定义具有外部链接的相同变量也总是不正确的.

要获得与C++相同的行为,您可以使用-fno-common带有gcc 的开关,这将导致相同的错误.(如果您正在使用GNU链接器但不使用,-fno-common您可能还需要考虑使用--warn-common/ -Wl,--warn-common选项突出显示遇到多个具有相同名称的常见和非常见符号时的链接时间行为.)

从gcc手册页:

-fno-common

在C代码中,控制未初始化的全局变量的放置.Unix C编译器传统上允许通过将变量放在公共块中而在不同的编译单元中对这些变量进行多种定义.这是由-fcommon大多数目标指定的行为,并且是GCC的默认行为.另一方面,ISO C不要求这种行为,并且在某些目标上可能会对变量引用带来速度或代码大小的损失.该-fno-common选项指定编译器应将未初始化的全局变量放在目标文件的数据部分中,而不是将它们生成为公共块.这样做的结果是,如果extern在两个不同的编译中声明(不)相同的变量,则在链接它们时会出现多重定义错误.在这种情况下,您必须使用编译-fcommon.编译 -fno-common对于提供更好性能的目标非常有用,或者如果您希望验证程序是否可以在其他系统上运行,这些系统始终以这种方式处理未初始化的变量声明.

gcc的行为是常见的,它在标准的附件J(非规范性)中描述,它描述了标准的常用扩展:

J.5.11多个外部定义

对象的标识符可能有多个外部定义,无论是否明确使用关键字extern; 如果定义不一致,或者初始化了多个,则行为未定义(6.9.2).