C语言中枚举类型的溢出问题?

9
如果我有一个枚举类型,比如: enum week {sunday=0, monday, tuesday, wednesday, thursday, friday, saturday}; 我还有:
enum week day;
day = saturday;
day++;

day的值将会是什么?


1
这似乎是你可以通过在两秒钟内检查编译器来解决的问题 printf("%d\n", ++(day = saturday));。当然,如果你必须问,那么你可能不应该将它放入程序中,因为每次阅读它时都保证看起来像一个错误。 - Parthian Shot
3
它能编译和执行并不保证其行为是被定义的,尽管如此,我认为你贴出的代码将会 始终 打印7,但我更愿意看到规范中的描述。 - fzzfzzfzz
大致相关:https://dev59.com/5m_Xa4cB1Zd3GeqP0mm1 - alk
在使用枚举时,最好的做法是只将已声明的名称分配给枚举实例。因此,++、--、2等都是不良的编程习惯,不应该使用。 - user3629249
3个回答

4
一个枚举类型本质上是一个命名的整数值。该枚举类型与能够表示所有命名值的基础整数类型相关联。该基础整数类型必须能够表示所有唯一的命名值,但其实际类型是由实现定义的。
在这种情况下,`saturday` 的数字值将为 `6`。将其递增将给出数字值 `7`。在实践中,这不太可能溢出基础整数类型(`int`、`char`、`unsigned char` 或编译器选择的其他类型),因此使用 `%d` 格式打印该值将打印 `7`。
然而,没有类型为 `enum week` 且值为 `7` 的枚举(命名)值。
如果递增枚举值会导致基础整数类型溢出(这里不是这种情况),则结果未定义。这是因为基础整数类型可以是有符号或无符号的,并且溢出有符号整数类型会产生未定义行为。
理论上,编译器可能会为 `enum week` 使用一个基础类型,该类型仅能表示值 `0` 到 `6` - 在这种情况下,递增 `saturday` 将产生未定义行为。在实践中,据我所知,还没有任何 C 编译器选择基础类型为标准整数类型(`char`、`int`、`unsigned char`、`unsigned` 等)之外的类型。所有这些类型都可以表示数字值 `7`。

1
@MarkRansom:标准确实防止了这样的调试模式,或者至少规定它是不符合要求的。day++之后day的值是7。符合规范的实现可以自由地发出编译时警告(关于任何事情),但是除了将值7存储在day中之外,在运行时不能做任何其他操作。当然,为了施加其他检查,编译器可以提供不符合标准的模式。 - Keith Thompson
1
@MarkRansom:我相信我的回答是符合要求的。如果不是,我错过了什么?(我刚刚添加了一个新的最后一段。) - Keith Thompson
1
@MarkRansom:int n = 6; n++; -- 我认为我们可以达成一致,即符合规范的实现不允许在 n 中存储除 7 以外的任何值。enum week day = saturday; day ++; -- 由于 enum day 与某些实现定义的整数类型兼容,并且任何实现选择的类型都必须能够存储值 7,因此符合规范的实现 必须day 中存储值 7。您在标准中看到了允许它执行其他操作的内容吗? - Keith Thompson
1
@Peter:我不认为我是这样的。例如,正好有256个成员的枚举类型可能由“unsigned char”支持,并且只能表示这256个值(假设“CHAR_BIT==8”)。但是,能够表示“6”的整数类型必须能够表示“7”,因为需要纯二进制表示。 - Keith Thompson
1
@Peter C99 6.2.6.2(太长了不方便在此贴出)规定,能够表示6的任何类型也必须能够表示7。 - martinkunev
显示剩余6条评论

2
给定:
enum week {
    sunday=0, monday, tuesday, wednesday, thursday, friday, saturday
};
enum week day = saturday;
day ++;

day的值为7

引用2011年ISO C标准6.7.2.2第1段:

每个枚举类型都应与char、有符号整数类型或无符号整数类型兼容。类型的选择是实现定义的,但应能表示枚举所有成员的值。

_Bool是一种无符号整数类型,但它不满足这个特定枚举类型的要求。

由于CHAR_BIT的值至少为8,并且要求类型charunsigned charsigned char没有填充位,因此每种字符类型的范围必须至少涵盖0127。更宽的整数类型(shortint等)的范围至少与signed charunsigned char相同。因此,与enum week兼容的实现定义类型的下限不得大于0,上限不得小于127

警告:下面进入语言解释阶段)可能存在一种漏洞,允许使用比 _Bool 更宽但比 char 更窄的扩展整数类型。例如,我认为大小为8位但只有3个值位的扩展整数类型是合法的。但由于整数类型必须使用二进制表示,具有3个值位的无符号类型将能够表示从0到7的值,而具有2个值位的无符号类型将无法表示saturday的值。由于enum week可以保存值6,因此它也必须能够保存值7。在一个不寻常的实现中,它可能无法表示值8,但你不太可能遇到这样的实现。

基本上,鉴于整数类型必须使用纯二进制表示,任何能够表示6的类型也能够表示7,尽管它并不自动表示也能表示8


一个自定义类型,它只有5个值位可以表示3个值(012),为什么会被禁止? - Lover of Structure
关于这一点,我刚刚更新了我的问题不同枚举类型之间的范围和可转换性。如果您有机会看一下,那就太好了。 - Lover of Structure
1
更正之前的评论,修复了一个打字错误。@LoverofStructure 如果一个整数类型有5个值位,那么它至少可以表示32个不同的值。如果是无符号的,这些值就是0..31。如果是有符号的,它可以表示所有这些值加上31或32个负值(符号位不是值位)。请参见N1570第6.2.5.2段第1句。根据填充位的值可能会有陷阱表示,但对于每个值0..31,都有一组值和填充位的组合来表示该值。 - Keith Thompson

1
我只能找到C89规范的草案,并且我不是C专家,所以我可能会误解它。但是它的3.5.2.2部分说:

枚举列表中的标识符被声明为具有int类型的常量,并且可以出现在允许这样做的任何地方。[...]每个枚举类型都应与整数类型兼容。

我认为这意味着day++将始终产生7(比saturday表示的值多一)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接