假设我有(在 32 位机器上)
enum foo {
val1 = 0x7FFFFFFF, // originally '2^31 - 1'
val2,
val3 = 0xFFFFFFFF, // originally '2^32 - 1'
val4,
val5
};
val2、val4 和 val5 的值是多少?我知道我可以测试它,但结果是否标准化?
在 C 标准中:
C11 (n1570), § 6.7.2.2 枚举说明符
每个枚举类型应与
char
有符号整数类型或无符号整数类型兼容。类型的选择是实现定义的,但应该能够表示枚举的所有成员的值。
如果编译器使用的基础类型不能表示这些值,则行为未定义。
C11 (n1570), § 4. 一致性
如果违反了出现在约束或运行时约束之外的“应”或“不应”要求,则行为未定义。
从 C++11 标准(§7.2,6,强调我的):
对于底层类型不固定的枚举,底层类型是一个整数类型,可以表示枚举中定义的所有枚举值。如果没有整数类型可以表示所有枚举数值,则枚举格式错误。使用哪种整数类型作为基础类型由实现定义,除非基础类型不得大于 int,除非枚举数的值不能适合 int 或 unsigned int。
因此,如果有一个大于 32 位的整数类型,编译器会很乐意做正确的事情。如果不是,则枚举格式不正确。不会有环绕。
这些值将是:
enum foo {
val1 = 0x7FFFFFFF,
val2, // 0x80000000 = 2^31
val3 = 0xFFFFFFFF,
val4, //0x0000000100000000 = 2^32
val5 //0x0000000100000001 = 2^32+1
};
不断增加的数字也得到了很好的定义(§7.2,2):
[...]没有初始化器的枚举器定义为枚举器提供了通过将前一个枚举器的值增加一而获得的值。
5.2.4.2.1要求int
至少16位宽;AFAIK 没有上限(long
但必须更长或等于 6.2.5 /8)。
6.5 /5:
如果在计算表达式期间出现异常情况(即,如果结果未在数学上定义或不在其 type 的可表示值范围内),则行为未定义。
那么 OP 中的示例违反了约束 6.7.2.2 /2:
定义枚举常量值的表达式应为整数常量表达式,其值可表示为
int
.
此外,枚举数被定义为6.7.2.2 /3int
类型的常量:
枚举器列表中的标识符被声明为具有类型的常量,
int
并且可以出现在任何允许的地方。
请注意,枚举的类型和枚举数/枚举常量的类型是有区别的:
enum foo { val0 };
enum foo myVariable; // myVariable has the type of the enumeration
uint_least8_t v = val0*'c'; // if val0 appears in any expression, it has type int
在我看来,这允许缩小,例如将枚举类型的大小减少到 8 位:
enum foo { val1 = 1, val2 = 5 };
enum foo myVariable = val1; // allowed to be 8-bit
但它似乎不允许扩大,例如
enum foo { val1 = INT_MAX+1 }; // constraint violation AND undefined behaviour
// not sure about the following, we're already in UB-land
enum foo myVariable = val1; // maximum value of an enumerator still is INT_MAX
// therefore myVariable will have sizeof int
由于 6.7.2.2 /3,
[...]每个后续的枚举数都
=
将其枚举常量定义为通过将前一个枚举常量的值加 1 获得的常量表达式的值。[...]
UB中的示例结果:
enum foo {
val0 = INT_MAX,
val1 // equivalent to `val1 = INT_MAX+1`
};
这是 C++ 的答案:在 7.2/6 中,它指出:
[...] 底层类型是一个整数类型,可以表示枚举中定义的所有枚举值。如果没有整数类型可以表示所有枚举数值,则枚举格式错误。使用哪种整数类型作为基础类型由实现定义,除非基础类型不得大于 int,除非枚举数的值不能适合 int 或 unsigned int。
因此,与 C 相比:如果编译器找不到类型,则没有未定义的行为,并且编译器不能只将其 512 位扩展整数类型用于您的二值枚举。
这意味着在您的示例中,基础类型可能是一些带符号的 64 位类型 - 大多数编译器总是首先尝试类型的带符号版本。