C99标准要求用于定义枚举常量值的表达式具有可表示为int
的值。
在部分6.7.2.2C99标准的第2段中:
定义枚举常量值的表达式应为整数常量表达式,其值可表示为**int
。
然而,枚举类型可以由实现定义为与任何整数类型兼容,包括具有int
之外的值范围的整数类型。
在部分6.7.2.2C99标准的第2段中:
每个枚举类型应与char
**、有符号整数类型或无符号整数类型兼容。
这意味着虽然不能显式地将枚举常量的值设置在int
的范围之外,但如果实现将枚举类型定义为与范围在int
之外的整数类型兼容,则枚举常量的值可以在int
的范围之外。
现在我知道了一种方法来获取枚举常量int
范围之外的特定值:* 虚拟枚举器 *。
enum hack{
DUMMY0 = INT_MAX,
DUMMY1,
/* supply as many more dummy enumerators as needed */
...
/* declare desired enumerator */
FOOBAR
};
这要归功于6.7.2.2C99标准的第3段部分www.example.com:
具有=
的枚举器将其枚举常量定义为常量表达式的值。
...
不带=
的每个后续枚举数将其枚举常量定义为常量表达式的值,该表达式是通过将前一个枚举常量的值加1获得的。
不幸的是,这只适用于大于INT_MAX
的正值,因为每个后续枚举器的值只递增。另一个警告是需要创建可能多个 *dummy enumerator * 来获取所需的特定枚举器。
这就引出了以下问题:
1.有没有办法将枚举常量的值设置为int
范围之外的负值?
1.是否有更好的方法将int
范围外的正值设置为枚举常量?
1.关于我的 dummy enumerator hack,C99标准是否限制了可以在单个enum
中声明的枚举数?
3条答案
按热度按时间8yoxcaq71#
如何设置枚举常量的值在int范围之外?
(目前,如果你使用的是C23或更高版本的编译器,请参阅下面的UPDATE 2)。
C99标准要求用于定义枚举常量值的表达式具有可表示为
int
的值。是的,C11标准没有改变这一点。
然而,枚举类型可以由实现定义为与任何整数类型兼容,包括那些值范围在
int
之外的整数类型。同样正确。
这意味着,虽然不能显式地将枚举常量的值设置在
int
的范围之外,但如果实现将枚举类型定义为与范围在int
之外的整数类型兼容,则枚举常量的值可以在int
的范围之外。这是不正确的,但我认为你已经发现了标准中措辞的弱点。(更新:我不认为这真的是一个弱点;见下文)。你引用了6.7.2.2:
定义枚举常量值的表达式应为整数常量表达式,其值可表示为int。
这似乎只适用于由 explicit 表达式定义的值,而不是像这样的情况:
但这实际上并不起作用,因为
even_bigger
被声明为int
类型的常量,显然它的值不能为INT_MAX + 1
。我强烈怀疑 * 意图 * 是上面的声明是非法的(违反约束);也许6.7.2.2应该重新措辞,使其更清晰。(更新:我现在认为这已经足够清楚了;见下文)
GCC的作者似乎同意我的观点:
因此,即使您解释是正确的,也不太可能编写和使用依赖于它的代码。
一个解决方法是使用整数(枚举类型或多或少都是伪装的整数)。不幸的是,
const
整型对象不是常量表达式,所以你可能不得不求助于使用预处理器:这假设
long long
比int
宽,这是可能的,但不能保证(如果int
至少是64位,则int
和long long
可以是相同的大小)。你的问题1和2的答案是否定的;你不能定义一个枚举常量,* 无论是负的还是正的 *,在
int
的范围之外。至于你的问题3,5.2.4.1C11标准的www.example.com部分(粗略地)说,编译器必须在单个枚举中支持 * 至少 * 1023个枚举常量。大多数编译器实际上并没有强加一个固定的限制,但在任何情况下,所有的常量都必须具有
INT_MIN
范围内的值。INT_MAX
,所以这对你没有多大好处。(同一类型的多个枚举常量可以具有相同的值。)(The翻译限制要求实际上比这更复杂。编译器必须支持 * 至少一个 * 程序,该程序包含所有枚举限制列表的至少一个示例。这是一个相当无用的要求,如所述。其目的是,满足标准要求的最简单方法是避免施加任何固定限制。)
更新:
我在comp.std.c Usenet新闻组上提出了这个问题。Tim Rentsch在那次讨论中提出了一个很好的观点,我现在认为这是:
是约束冲突,需要编译器诊断。
我担心的是禁止
int
范围之外的 * 显式 * 值的措辞:定义枚举常量值的表达式应为整数常量表达式,其值可表示为int。
不适用,因为不涉及(显式)* 表达式 。但是6.7.7.2p3说:
没有*=的每个后续枚举器将其枚举常量定义为常量expression**的值,该常量通过将前一个枚举常量的值加1获得。
(强调部分另加)。因此,有一个 * 表达式 *,其值必须可表示为
int
;只是没有出现在源代码中。我不是100%满意,但我想说意图足够清楚。下面是关于comp.std.c的讨论。
更新二:
2023版的ISO C标准(在我写这篇文章时尚未发布,但最新的草案是N3054)允许 enum-type-specifier 指定枚举类型的底层类型。
gcc 13.1.0通过
-std=c2x
选项部分支持C23。在我的系统上,gcc 13.1.0的输出是:
jaql4c8m2#
也许我不能说任何基思汤普森没有告诉你,还没有。
不管怎样,我会尽力的。
1. int范围内的值
在ISO C99文件中,我可以在第6.7.2.2节第2和3段中看到以下声明:
(2)定义枚举常量值的表达式应为整数常量表达式,其值可表示为int。
如果你写
enum T { X = (expr) } VAR
,那么expr
是一个int范围内的整数,其中至少包括范围 -32767。+32767,如您在5.2.4.2.1
中所读。当然,第**(2)**款并没有对标识符 X 的类型施加任何限制。
2.枚举器标识为int类型
在第3段中,我们可以读到这一行:
(3a)枚举数列表中的标识符被声明为int类型的常量,并且可以出现在任何允许的地方。
这是对标识符类型的限制。现在 X 的类型为int。
3.数值讨论
此外,该标准还规定:
(3b)一个枚举数与=内斯其枚举常量作为常量表达式的值。
但是这个句子现在受到**(2)和(3a)**的限制。
因此,我对标准C99的解释如下:如果你写
然后
BIGG
有类型int(根据**(3a))。正如基思Tompson所指出的,值(=“枚举常量?BIGG的“)不是来自 * 表达式 *,表示超出范围的值(对于int**)。它的值(在数学意义上)是 X+1,因为应用了下一个规则:
(3c)每个后续的枚举数no =内斯其枚举常量定义为常量表达式的值,该常量表达式通过将前一个枚举常量的值加1获得。
标准中没有任何规则(关于整数算术)定义这种情况下编译器的行为。因此,它将属于
implementation defined
类...然而,在编译器接受这个超出范围的值的情况下,我相信(数学)X+1 将被转换为int范围内的值。
但是,(3b)中的预期行为似乎是C表达式**(X+1)== BIGG总是true**。如果我是对的,那么我同意基思的观点,编译器必须用Out of range错误拒绝声明。
4.枚举类型的整数类型
我们可以读到更多:
(4a)每个枚举类型应与char、有符号整数类型或无符号整数类型兼容。
声明
enum T
定义了一个新的integer类型。此类型不一定与 expression
expr
的类型相关,也不一定与 enumeratorX
的类型相关。它只是另一种类型的另一件事:与我们正在定义的enum类型
T
相关联的整数类型。(This类型将是分配给变量
VAR_T
的类型)。实现可以决定哪种整数类型更合适。
如果表达式(如
expr
)的值非常小,这几乎总是发生的,那么编译器可能会决定T
的类型为char。如果出于某种原因需要long long,则
T
的类型将是long long,依此类推。但是,这并没有改变表达式
expr
和枚举器X
必须遵循的对int的限制。它们是int。expr
和T
类型之间的唯一关联规则是:(4b)类型的选择由实现定义,但应能够表示枚举的所有成员的值。
因此,如果你有
enum T { X = 0, Y = 5, Z = 9 }
,那么T
的类型可以是char。(The这种情况类似于一个字符常量,如
'c'
,它总是具有类型int,被传递给一个char变量:char c = 'c';
:虽然'c'
是一个int,但它的值在char的范围内)。另一方面,如果你有
enum T { X = 20202, Y = -3 }
,编译器不能为T
选择char。似乎int是T
的完美类型(对于任何enum类型),但编译器可以为T
选择任何其他整数类型,其范围包含值 20202 和 -3。如果none值为负,则编译器可以选择
unsigned
整数类型。5.摘要
总之,我们可以说:
1.一个enum声明涉及4类型:表达式(
expr
)、值(或枚举常量,来自表达式或仅为隐式)、枚举数(X
)和enum类型(T
)。1.表达式的类型(如
expr
)始终为int。值的类型似乎很难在int的范围内(INT_MAX+1
似乎不允许)。枚举器的类型为int(如X
)。而enum类型(如T
)是由实现在所有允许的整数类型中选择的,只是适合声明中的值。1.在表达式中,我们有:
#define NN 5
enum T { X = 0. Y = 3, Z = (NN*3) } evar;
表达式
(NN * 3) + 1
是int。表达式
(Z + 1)
是int。如果编译器将
T
定义为char,那么表达式
((evar = Z), ++evar)
是一个char。i34xakig3#
标准是
...the value of the constant expression obtained by adding 1 to the value...
。因此,非显式表达式“value”也被认为来自常量表达式。
然而,为了确保只允许int“values”,我们必须共同考虑第2段声明的限制:
The expression that defines the value of an enumeration constant ... has a value representable as an int
。因此,(非显式表达式)值也必须适合int。
我现在确信,预期的约束是每个枚举器值都适合
int
。