我在C项目中遇到了以下构造,我将其移植到C++;
enum TestEnum 
{
    A=303,
    B=808
} _TestEnum;
int foo()
{
  _TestEnum = B;
}
在使用GCC进行编译并查看生成的代码时,我得到:
nils@doofnase ~ $ gcc -std=c90 -O2 -c ./test.c -o test.o
nils@doofnase ~ $ size test.o
   text    data     bss     dec     hex filename
     59       0       0      59      3b test.o
因此使用零字节数据或BSS段.
另一方面,如果我用C++编译,我得到:
nils@doofnase ~ $ g++ -std=c++11 -O2 -c ./test.c -o test.o
nils@doofnase ~ $ size test.o
   text    data     bss     dec     hex filename
     59       0       4      63      3f test.o
我看到BSS中分配了四个字节的存储空间,正如我所料.
此外,在C项目中,枚举定义实际上位于头文件中,该头文件包含在多个c文件中.该项目编译和链接就好了.当编译并链接为C++时,编译器会抱怨_TestEnum是在多个对象中定义的(就这样!).
这里发生了什么?我在看一些古老的C语言特例吗?
编辑:为了完整起见,这是gcc版本:
nils@doofnase ~ $ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
Grz*_*ski 11
默认情况下,GCC编译器启用C 扩展(即使-pedantic标志生效),它允许跨翻译单元的多个对象外部定义.
参考C11(N1570)J.5.11 多个外部定义(资料部分):
对象的标识符可能有多个外部定义,无论是否明确使用关键字
extern; 如果定义不一致,或者初始化了多个,则行为未定义(6.9.2).
请注意,依赖于此行为的应用程序并不严格符合ISO C语言.更具体地说,C11 6.9/p5 外部定义陈述(强调我的):
一个外部定义为外部声明,这也是一个功能(比内联定义其他)或对象的定义.如果在表达式中使用通过外部链接声明的标识符(除了作为 结果为整数常量的运算符
sizeof或_Alignof操作符的操作数的一部分),则整个程序中的某个地方应该只有一个标识符的外部定义; 否则,不得超过一个.161)
从技术上讲,违反该规则会调用未定义的行为,这意味着实现可能会也可能不会发出诊断消息.
您可以通过nm命令检查此扩展是否已启用:
nm test.o 
0000000000000000 T foo
0000000000000004 C _TestEnum
根据man nm:
"C"符号很常见.常用符号是未初始化的数据.链接时,多个常用符号可能会出现相同的名称.如果符号在任何地方定义,则公共符号将被视为未定义的引用.
要禁用此扩展,您可以使用-fno-common标志.来自GCC文件:
Unix C编译器传统上为公共块中的未初始化全局变量分配存储.这允许链接器将不同编译单元中相同变量的所有暂定定义解析为同一对象,或解析为非暂定定义.这是由
-fcommon大多数目标指定的行为,并且是GCC的默认行为.