枚举常量在C和C ++中表现不同

时间:2017-01-24 18:46:37

标签: c++ c

为什么这样:

#include <stdio.h>
#include <limits.h>
#include <inttypes.h>

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);
}

在C中打印4 8 8,在C ++中打印8 8 8(在4字节整数的平台上)?

我的印象是UINT64_MAX赋值会强制所有枚举常量至少为64位,但en_e_foo在纯C中保持为32。

这种差异的理由是什么?

6 个答案:

答案 0 :(得分:80)

在C中,Tasks -> Generate Scripts常量的类型为enum。在C ++中,它是枚举类型。

int

在C中,这是一个约束违规,需要诊断(如果 enum en_e{ en_e_foo, en_e_bar=UINT64_MAX, }; 超过UINT64_MAX,它很可能会这样做)。 C编译器可以完全拒绝该程序,或者它可以打印警告然后生成其行为未定义的可执行文件。 (并不是100%明确表示违反约束的程序必然具有未定义的行为,但在这种情况下,标准并未说出行为是什么,因此仍然是未定义的行为。)

gcc 6.2并没有对此发出警告。铿锵的。这是gcc中的一个错误;当使用来自标准头的宏时,它会错误地禁止某些诊断消息。感谢Grzegorz Szpetkowski找到错误报告:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613

在C ++中,每个枚举类型都有一个底层类型,它是一些整数类型(不一定是INT_MAX)。此基础类型必须能够表示所有常量值。因此,在这种情况下,inten_e_foo都属于en_e_bar类型,即使en_e更窄,也必须至少为64位宽。

答案 1 :(得分:26)

该代码首先不是有效的C.

C99和C11中的第6.7.2.2节说:

  

<强>约束:

     

定义枚举常量值的表达式应为整数常量表达式,其值可表示为int

编译器诊断是强制性的,因为它是违反约束的,请参阅5.1.1.3:

  

如果预处理转换单元或转换单元包含违反任何语法规则或约束的情况,则符合要求的实现应生成至少一条诊断消息(以实现定义的方式标识),即使该行为也明确指定为未定义或实施定义。

答案 2 :(得分:24)

C 中,虽然enum被视为单独的类型,但枚举器本身的类型始终为int

  

C11 - 6.7.2.2枚举说明符

     

3 枚举器列表中的标识符声明为类型为int的常量...

因此,您看到的行为是编译器扩展。

我认为如果其值太大,只扩展其中一个枚举数的大小是有道理的。

另一方面,在 C ++ 中,所有枚举器都具有声明它们的enum类型。

因此,每个枚举器的大小必须相同。因此,扩展整个enum的大小以存储最大的枚举器。

答案 3 :(得分:16)

正如其他人指出的那样,由于违反约束,代码格式不正确(在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

答案 4 :(得分:12)

C11 - 6.7.2.2/2

  

定义枚举常量值的表达式应为整数常量表达式,其值可表示为int

en_e_bar=UINT64_MAX是违反约束的行为,这使上述代码无效。应通过确认C11草案中所述的实施来产生诊断信息:

  

如果预处理翻译单元或翻译单元包含违反任何语法规则或约束的情况,符合要求的实现应至少生成一条诊断消息(以实现定义的方式标识),[...]

GCC似乎有一些错误,但无法生成诊断消息。 (answer通过Grzegorz Szpetkowski

指出了错误

答案 5 :(得分:5)

我查看了标准,由于6.7.2.2p2,我的程序似乎是C中的约束违规:

  

约束:定义枚举常量值的表达式   是一个整数常量表达式,具有可表示为的值   中间体

并在C ++中定义因为7.2.5:

  

如果未修复基础类型,则每个枚举器的类型为   其初始化值的类型:    - 如果指定了初始化程序   对于枚举器,初始化值的类型与   表达式和常量表达式应该是一个整数常量   表达式(5.19)。    - 如果没有为第一个指定初始化程序   枚举器,初始化值具有未指定的整数类型。    - 否则初始化值的类型与类型相同   前面的枚举器的初始化值除非   增量值在该类型中不可表示,在这种情况下   type是一个未指定的整数类型,足以包含   递增的值。如果不存在此类型,则该程序格式不正确。