为什么这样:
#include#include #include int main() { enum en_e { en_e_foo, en_e_bar = UINT64_MAX, }; enum en_e e = en_e_foo; printf("%zu\n", sizeof en_e_foo); printf("%zu\n", sizeof en_e_bar); printf("%zu\n", sizeof e); }
4 8 8
用C和8 8 8
C++ 打印(在4字节整数的平台上)?
我的印象是,UINT64_MAX
赋值将强制所有枚举常量至少为64位,但en_e_foo
在纯C中保持为32.
这种差异的理由是什么?
在C中,enum
常量是类型int
.在C++中,它是枚举类型.
enum en_e{ en_e_foo, en_e_bar=UINT64_MAX, };
在C中,这是一个约束违规,需要诊断(如果 UINT64_MAX
超过INT_MAX
,它很可能会这样做).AC编译器可以完全拒绝该程序,或者它可以打印警告然后生成其行为未定义的可执行文件.(并非100%明确表示违反约束的程序必然具有未定义的行为,但在这种情况下,标准并未说明行为是什么,因此仍然是未定义的行为.)
gcc 6.2没有对此发出警告.铿锵的.这是gcc中的一个错误; 当使用来自标准头的宏时,它会错误地禁止某些诊断消息.感谢Grzegorz Szpetkowski查找错误报告:https://gcc.gnu.org/bugzilla/show_bug.cgi?id = 71613
在C++中,每个枚举类型都有一个底层类型,它是一些整数类型(不一定int
).此基础类型必须能够表示所有常量值.因此,在这种情况下,两个en_e_foo
和en_e_bar
的类型的en_e
,它必须是至少64个位宽,即使int
是窄.
该代码首先不是有效的C.
C99和C11中的第6.7.2.2节说:
约束:
定义枚举常量值的表达式应为整数常量表达式,其值可表示为
int
.
编译器诊断是必需的,因为它是约束违规,请参阅5.1.1.3:
如果预处理转换单元或转换单元包含违反任何语法规则或约束的情况,则符合要求的实现应生成至少一条诊断消息(以实现定义的方式标识),即使该行为也明确指定为未定义或实现 - 定义.
在C中,虽然a enum
被认为是一个单独的类型,但枚举器本身总是具有类型int
.
C11 - 6.7.2.2枚举说明符
3枚举器列表中的标识符声明为类型为int的常量...
因此,您看到的行为是编译器扩展.
我认为如果其值太大,只扩展其中一个枚举器的大小是有意义的.
另一方面,在C++中,所有枚举器都具有enum
声明它们的类型.
因此,每个枚举器的大小必须相同.因此,enum
扩展整数的大小以存储最大的枚举器.
正如其他人指出的那样,由于违反约束,代码格式不正确(在C中).
有GCC错误#71613(2016年6月报告),其中指出一些有用的警告用宏来沉默.
当使用来自系统头的宏时,有用的警告似乎被静音.例如,在下面的示例中,警告对两个枚举都有用,但只显示一个警告.其他警告也可能发生同样的情况.
目前的解决方法可能是使用一元运算+
符预先添加宏:
enum en_e { en_e_foo, en_e_bar = +UINT64_MAX, };
使用GCC 4.9.2在我的机器上产生编译错误:
$ gcc -std=c11 -pedantic-errors -Wall main.c main.c: In function ‘main’: main.c:9:20: error: ISO C restricts enumerator values to range of ‘int’ [-Wpedantic] en_e_bar = +UINT64_MAX
定义枚举常量值的表达式应为整数常量表达式,其值可表示为
int
.
en_e_bar=UINT64_MAX
是违反约束,这使上述代码无效.应通过确认C11草案中所述的实施来产生诊断信息:
如果预处理翻译单元或翻译单元包含违反任何语法规则或约束,则符合要求的实现应至少生成一条诊断消息(以实现定义的方式标识),[...]
似乎GCC有一些错误,它无法产生诊断信息.(错误指出在回答由格热戈日Szpetkowski
我查看了标准,由于6.7.2.2p2,我的程序似乎是C中的约束违规:
约束:定义枚举常量值的表达式应为整数常量表达式,其值可表示为int.
并且由于7.2.5在C++中定义:
如果基础类型不是固定的,则每个枚举器的类型是其初始化值的类型: - 如果为枚举器指定了初始化器,则初始化值与表达式具有相同的类型,而constant-expression应为整数常数表达式(5.19). - 如果没有为第一个枚举数指定初始值设定项,则初始化值具有未指定的整数类型. - 否则初始化值的类型与前一个枚举器的初始化值的类型相同,除非递增的值在该类型中不可表示,在这种情况下,类型是一个未指定的整数类型,足以包含递增的值.如果不存在此类型,则程序格式错误.