我在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
答案 0 :(得分:11)
默认情况下,GCC编译器启用C 扩展(即使-pedantic
标志生效),这允许translation units对象的多个外部定义。
参考C11(N1570)J.5.11 多个外部定义(资料性部分):
标识符的外部定义可能不止一个 一个对象,无论是否明确使用关键字
extern
;如果 定义不同意,或者不止一个被初始化, 行为未定义(6.9.2)。
请注意,依赖此行为的应用程序并不严格符合ISO C语言。更具体地说,C11 6.9 / p5 外部定义状态(强调我的):
外部定义是一个外部声明,也是一个 函数的定义(内联定义除外)或 宾语。如果使用外部链接声明的标识符 表达式(除了作为
sizeof
或_Alignof
的操作数的一部分之外 运算符,其结果是整数常量),在整个某处 程序应该只有一个外部定义 标识符;否则,不得超过一个。 161)
从技术上讲,违反该规则会调用undefined behavior,这意味着某个实现可能会也可能不会发出诊断消息。
您可以通过nm
命令检查此扩展名已启用:
nm test.o
0000000000000000 T foo
0000000000000004 C _TestEnum
根据man nm
:
" C"符号很常见。常用符号是未初始化的数据。 链接时,多个常用符号可能会出现相同的名称。 如果符号在任何地方定义,则将公共符号视为 未定义的参考文献。
要禁用此扩展程序,您可以使用-fno-common
标记。来自GCC文件:
Unix C编译器传统上为其分配存储空间 公共块中未初始化的全局变量。这允许 链接器解析同一变量的所有暂定定义 不同的编译单元到同一个对象,或非临时的 定义。这是
-fcommon
指定的行为,是 大多数目标的GCC默认值。