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

  • 本文关键字:存储 枚举 C++ 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

默认情况下,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 默认值。

最新更新