9

C99 标准要求用于定义枚举常量值的表达式具有可表示为int.

在C99 标准的第 6.7.2.2 节第 2 段中:

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

但是,枚举类型可以由实现定义为与任何整数类型兼容,包括那些值范围在int.

在C99 标准的第 6.7.2.2 节第 2 段中:

每个枚举类型应与char有符号整数类型或无符号整数类型兼容。

这意味着虽然您不能将枚举常量的值显式设置在 an 的范围之外,但如果实现将枚举类型定义为与具有范围的整数类型兼容,则int枚举常量的值可以在 an的范围之外int之外int


现在我知道了一种获取int枚举常量集合范围之外的特定值的方法:dummy enumerators

enum hack{
    DUMMY0 = INT_MAX,
    DUMMY1,
    /* supply as many more dummy enumerators as needed */
    ...
    /* declare desired enumerator */
    FOOBAR
};

这要归功于C99 标准的第 6.7.2.2 节第 3 段:

枚举器=将其枚举常量定义为常量表达式的值。
...
每个后续的枚举数没有= 定义其枚举常数为常数表达式的值,该值是通过在前一个枚举常数的值上加 1 获得的。

不幸的是,这仅适用于大于的正值INT_MAX,因为每个后续枚举数的值只会递增。另一个需要注意的是,可能需要创建许多虚拟枚举器来获取所需的特定枚举器。


这导致以下问题:

  1. 有没有办法将枚举常量的值设置为超出范围的负值int
  2. 有没有更好的方法来设置枚举常数范围之外的正值?int
  3. 关于我的虚拟枚举器黑客,C99 标准是否对可以在单个中声明的枚举器数量设置了限制enum
4

3 回答 3

10

如何将枚举常量的值设置在 int 范围之外?

你没有。

C99 标准要求用于定义枚举常量值的表达式具有可表示为int.

是的,C11 标准并没有改变这一切。

但是,枚举类型可以由实现定义为与任何整数类型兼容,包括那些值范围在int.

也正确。

这意味着虽然您不能将枚举常量的值显式设置在 an 的范围之外,但如果实现将枚举类型定义为与具有范围的整数类型兼容,则int枚举常量的值可以在 an的范围之外int之外int

这是不正确的,但我认为您发现标准中的措辞存在弱点。(更新:我不认为这真的是一个弱点;见下文)。您引用了 6.7.2.2:

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

这似乎仅适用于由显式表达式定义的值,而不适用于这样的情况:

enum too_big {
    big = INT_MAX,
    even_bigger
};

但这实际上不起作用,因为even_bigger它被声明为 type 的常量int,显然不能具有 value INT_MAX + 1

我强烈怀疑其意图是上述声明是非法的(违反约束);可能应该重新措辞 6.7.2.2 以使其更清楚。(更新:我现在认为已经很清楚了;见下文。)

gcc 的作者似乎同意我的观点:

$ cat c.c
#include <limits.h>
enum huge {
    big = INT_MAX,
    even_bigger
};
$ gcc -c c.c
c.c:4:5: error: overflow in enumeration values

因此,即使您的解释是正确的,您也不太可能编写和使用依赖于它的代码。

一种解决方法是使用整数(无论如何,枚举类型或多或少都是经过伪装的整数)。不幸的const是,整数对象不是常量表达式,因此您可能不得不求助于使用预处理器:

typedef long long huge_t;
#define big ((huge_t)INT_MAX)
#define even_bigger (big + 1)

这假设long long比 宽int,这很可能但不能保证(如果至少为 64 位,则int大小long long可能相同)。int

您的问题 1 和 2 的答案是否定的;不能在int.

至于您的问题 3,C11 标准的第 5.2.4.1 节(大致)说编译器必须在单个枚举中支持至少1023 个枚举常量。大多数编译器实际上并没有施加固定的限制,但在任何情况下,所有常量都必须具有范围内的值INT_MIN.. INT_MAX,所以这对你没有多大好处。(同一类型的多个枚举常量可以具有相同的值。)

(翻译限制要求实际上比这更复杂。编译器必须支持至少一个程序,该程序包含所有枚举限制列表的至少一个实例。如上所述,这是一个相当无用的要求。目的是最简单的方法满足标准给出的要求是避免施加任何固定限制。)

更新 :

我在 comp.std.c Usenet 新闻组上提出了这个问题。Tim Rentsch 在那次讨论中提出了一个很好的观点,我现在认为:

enum too_big {
    big = INT_MAX,
    even_bigger
};

是违反约束的,需要编译器诊断。

我担心的是禁止在范围之外的明确int值的措辞:

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

不适用,因为不涉及(显式)表达式。但是 6.7.7.2p3 说:

后面的每个没有=的枚举数都将其枚举常量定义为前一个枚举常量的值加 1 得到的常量表达式的值。

(强调补充)。所以有一个表达式,其值必须可以表示为int; 它只是没有出现在源代码中。我对此不是 100% 满意,但我会说意图足够明确。

这是关于 comp.std.c的讨论。

于 2013-08-06T21:36:46.213 回答
3

也许我不能说 Keith Thompson 还没有告诉你的任何事情。
反正我会努力的。

1. int 范围内的值

ISO C99的文档中,我可以在第6.7.2.2节第 2 和第 3 段中看到以下语句:

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

如果你写enum T { X = (expr) } VAR, thenexpr是 int 范围内的一个整数,它至少包括-32767 .. +32767范围,你可以在5.2.4.2.1.
当然,第(2)款对标识符X的类型没有任何限制。

2. 枚举器标识符的类型为 int

在第3段中,我们可以看到这一行:

(3a) 枚举器列表中的标识符被声明为具有 int 类型的常量,并且可以出现在任何允许的地方。

这是对标识符类型的限制。现在X的类型为int

三、价值观讨论

此外,该标准还说:

(3b) 带有 = 的枚举数将其枚举常量定义为常量表达式的值。

但是现在这句话受到(2)(3a)的限制。
所以,我对标准 C99 的解释如下:如果你写

 enum T { X = INT_MAX, BIGG}  

然后BIGG具有int类型(根据(3a))。
正如基思汤普森指出的那样,BIGG的值(= “枚举常数?”)不是来自表达式,表示超出范围的值(对于int)。它的值(在数学意义上)是X+1,因为应用了下一个规则:

(3c) 后面的每个没有 = 的枚举数将其枚举常数定义为常数表达式的值,该常数表达式是通过在前一个枚举常数的值上加 1 得到的。

在这种情况下,标准中没有任何规则(关于整数算术)定义编译器的行为。所以,它会属于implementation defined类......
但是,如果编译器接受这个超出范围的值,我相信(数学)X+1将被转换为int范围内的值。

但是,(3b)中的预期行为似乎是C表达式(X+1) == BIGG始终为true。如果我是对的,那么我同意 Keith,编译器必须以超出范围错误拒绝声明。

4.枚举类型的整数类型

我们可以阅读更多:

(4a) 每个枚举类型应与 char、有符号整数类型或无符号整数类型兼容。

该声明enum T定义了一个新的整数类型
这种类型不一定与表达式 expr的类型相关,也与枚举数 X的类型无关。
它只是另一种类型:与我们定义的枚举类型相关联的整数类型。 (这种类型将是分配给变量的类型)。 T
VAR_T

实现可以决定哪种整数类型更合适。
如果表达式 (like expr) 具有非常小的值,几乎总是发生,那么编译器可能会决定它Tchar类型,例如。
如果由于某种原因需要long long,则类型T将为long long,依此类推。

但是,这不会改变表达式和枚举数必须遵循的对int的限制。他们是int。与和 的类型相关的唯一规则是:exprX
exprT

(4b) 类型的选择是实现定义的,但应该能够表示所有枚举成员的值。

因此,如果您有enum T { X = 0, Y = 5, Z = 9 },那么 of 的类型T可以是char
(这种情况类似于将'c'始终具有 int 类型字符常量 like传递给char变量的情况char c = 'c';:虽然'c'int,但它的值适合char的范围)。

另一方面,如果你有,编译器enum T { X = 20202, Y = -3 }不能选择charT似乎intT(对于任何枚举类型)的完美类型,但编译器可以选择任何其他整数类型,T其范围包含值20202-3

如果没有值是负数,那么编译器可以选择unsigned整数类型。

五、总结

总而言之,我们可以说:

  1. 枚举声明中涉及4种类型:表达式 ( )、值(或枚举常量,来自表达式或只是隐式)、枚举器 ( ) 和枚举类型 ( )。 exprXT
  2. 表达式的类型 (as expr) 始终是int。值的类型似乎几乎不打算在int的范围内(INT_MAX+1似乎是不允许的)。枚举数的类型是int (as X)。并且枚举类型( as T)由实现选择,在允许的所有可能的整数类型中,只适合声明中的值。
  3. 在表达式中,我们有:

    #define NN 5
    enum T { X = 0. Y = 3, Z = (NN*3) } evar;

表达式(NN * 3) + 1是一个int
表达式(Z + 1)是一个int
如果编译器定义Tchar
则表达式((evar = Z), ++evar)char

于 2013-08-21T16:05:28.923 回答
1

回应 Keith Thompson 最近的更新

我认为你是对的:标准说...the value of the constant expression obtained by adding 1 to the value....
因此,非显式表达式“值”也被认为来自常量表达式

但是,为了确保只允许int “值”,必须共同考虑第 2 段声明的限制:The expression that defines the value of an enumeration constant ... has a value representable as an int.
因此,(非显式表达式)值也必须适合int

我现在确信,预期的约束是每个枚举器值都适合int

谢谢你,基思。

于 2013-08-21T16:48:20.743 回答