假设以下代码:
交流转换器:
#include <stdio.h>
int a;
int func();
int main(int argc, char **argv) {
a = 7;
int a2 = func();
printf("a is %d, a2 is %d\n", a, a2);
return 0;
}
和b.c:
int a;
int func()
{
a = 9;
return a;
}
使用g++ a.c b.c -Wall -O0
编译时,会产生链接错误,如预期的那样。但是,在调用gcc a.c b.c -Wall -O0
时,它不会产生任何警告,也不会产生错误!
顺便说一下,输出是a is 9, a2 is 9
。
gcc版本5.4.0 20160609(Ubuntu 5.4.0-6ubuntu1~16.04.4)
为什么海湾合作委员会允许这样做? 我对这种行为感到惊讶。如果在声明时初始化变量,那么链接也将失败并使用GCC。
答案 0 :(得分:5)
使用
g++ a.c b.c -Wall -O0
编译时,会产生链接错误,如预期的那样。但是,在调用gcc a.c b.c -Wall -O0
时,它不会产生任何警告,也不会产生错误!
在您的代码中a
具有暂定定义(在当前翻译单元的末尾变为完整定义),该定义在C中有效。但是,还有另一个这样的暂定定义,它在b.c
中成为另一个翻译单元的完整定义 - 两者都为你的程序中的a
提供了一个外部定义。换句话说,a.c
和b.c
本身就可以了,但是当它们组合在一起时(无论是直接编译还是将它们编译成单独的模块然后通过链接他们)。这是未定义的行为:
外部定义是外部声明,它也是函数(内联定义除外)或对象的定义。如果在表达式中使用通过外部链接声明的标识符(除了作为sizeof或_Alignof运算符的操作数的一部分,其结果是整数常量),则整个程序中的某个地方应该只有一个标识符的外部定义;否则,不得超过一个.161
然而,这通常被gcc作为扩展支持。这就是为什么当你调用gcc
时,它编译为C代码。严格(标准),这是C中的无效代码。
当您调用g++
将其编译为C ++代码时,它会失败,因为C ++没有暂定定义。它在C ++中无效。因此,g++
出错了。请参阅C ++中的One Definition Rule。