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

时间:2013-08-06 21:09:56

标签: c enums enumeration c99 language-lawyer

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

第6.7.2.2节 C99标准第2段中:

  

定义枚举常量值的表达式应为整数   具有可表示为 int 的值的常量表达式。

但是,实现可以定义枚举类型以与任何整数类型兼容,包括范围超出int的值。

第6.7.2.2节 C99标准第2段中:

  

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

这意味着虽然您无法在int范围之外显式设置枚举常量的值,但如果实现定义,则枚举常量的值可能超出int的范围枚举类型与范围超出int的整数类型兼容。


现在我知道一种获取枚举常量int范围之外的特定值的方法:虚拟枚举器

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. 关于我的虚拟枚举器 hack,C99标准是否对可以在单个enum中声明的枚举数设置了限制?

3 个答案:

答案 0 :(得分:10)

  

如何设置枚举常量的值超出int范围?

你没有。

  

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

是的,C11标准没有改变任何一个。

  

但是,枚举类型可以由实现定义   兼容任何整数类型,包括范围为   值int以外的值。

也正确。

  

这意味着虽然您无法明确设置值   枚举常量超出int范围,a的值   枚举常量可以在int的范围之外   实现定义了与a兼容的枚举类型   范围超出int的整数类型。

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

  

定义枚举常量值的表达式   是一个整数常量表达式,具有可表示为的值   的 INT

似乎仅在值由显式表达式定义时才适用,而不是像这样的情况:

enum too_big {
    big = INT_MAX,
    even_bigger
};

但这实际上并不起作用,因为even_bigger被声明为int类型的常量,显然不能具有值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,这可能但不能保证(如果intlong long,则intint的大小可能相同至少64位)。

你的问题1和2的答案是否定的;你不能在INT_MIN范围之外定义枚举常量,为负数或正数

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

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

更新:

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

int

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

我担心的是禁止显式值超出int范围的措辞:

  

定义枚举常量值的表达式   是一个整数常量表达式,具有可表示为的值   的 INT

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

  

没有 = 的每个后续枚举器定义其枚举   常量为通过加1获得的常量表达式的值   到前一个枚举常量的值。

(重点补充)。所以有一个表达式,其值必须可以表示为{{1}};它只是没有出现在源代码中。我对此并不十分满意,但我认为其意图非常明确。

这是comp.std.c上的discussion

答案 1 :(得分:3)

也许我不能说Keith Thompson没有告诉你的任何事情 无论如何,我会尝试。

<强> 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的解释如下: 如果你写

 enum T { X = INT_MAX, BIGG}  

然后BIGG的类型为 int (根据(3a))。
正如Keith Tompson指出的那样, BIGG 的值(= &#34;枚举常量?&#34; )不是来自表达式,表示超出范围的值(对于 int )。它的值(在数学意义上)是 X + 1 ,因为应用了下一个规则:

  

(3c)每个后续的枚举器没有=   将其枚举常量定义为得到的常量表达式的值   将1添加到上一个枚举常量的值。

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

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

<强> 4。枚举类型的整数类型

我们可以阅读更多内容:

  

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

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

实现可以决定哪种整数类型更合适 如果表达式(如expr)的值非常小,几乎总是如此, 然后编译器可以决定T的类型为 char ,例如 如果由于某种原因需要很长, 那么T的类型将长很长,依此类推。

但是,这不会更改 int 的限制,表达式expr和枚举器X必须遵循。它们是 int
exprT类型相关的唯一规则是:

  

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

因此,如果您有enum T { X = 0, Y = 5, Z = 9 },则T的类型可以是字符
(这种情况类似于一个字符常量,如'c',总是将类型 int 传递给 char 变量:{{1虽然char c = 'c'; int ,但其值适合 char 的范围。

另一方面,如果您有'c',则编译器无法为enum T { X = 20202, Y = -3 }选择 char 。似乎 int T的完美类型(对于任何 enum 类型),但编译器可以为{{1}选择任何其他整数类型其范围包含值 20202 -3

如果没有值为负,则编译器可以选择T整数类型。

<强> 5。摘要

总之,我们可以这样说:

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

    X
    T

  4. 表达式#define NN 5 int
    表达式enum T { X = 0. Y = 3, Z = (NN*3) } evar; int 如果编译器将(NN * 3) + 1定义为 char ,则 然后表达式(Z + 1) char

答案 2 :(得分: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

谢谢你,基思。