枚举C与C ++的存储差异

时间:2018-01-06 10:09:36

标签: c++ c enums

我在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

1 个答案:

答案 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默认值。