无论枚举值的数量如何,常量枚举大小保持不变

12

无论枚举类型中枚举值的数量有多少,在16位或32位体系结构上,enum类型的大小始终为2或4个字节。

enum是否像union一样被编译器处理?


2
@GrijeshChauhan:在C语言中是True,但在C++中不是——并且在C语言中,枚举常量的大小可以与类型的大小不同。 - Keith Thompson
2
如果你创建了 2,147,483,648 个枚举,会发生什么?我敢打赌编译器会变得很不高兴... - Jiminion
3
@Jim,我也有同样的疑问,没有人想创建超过100个符号常量。如果他这样做,那么他可能是一个糟糕的程序员。 - Grijesh Chauhan
7
如果你假定典型的大写字母 enum 标识符,并保持统一长度,每个标识符都必须是7个字符,再加上逗号和空格,这意味着源文件本身将达到约20GB。我不认为有许多编译器会愉快地尝试处理这个问题... - twalberg
1
@LorenPechtel: enum too_big { big = INT_MAX, bigger }; @LorenPechtel:enum too_big { big = INT_MAX, bigger }; - Keith Thompson
显示剩余9条评论
6个回答

20
在C和C++编程语言中,枚举类型的大小是由实现定义的,并且与某些整数类型的大小相同。通常的做法是将所有枚举类型的大小都设置为与int类型相同,因为这通常是最有效访问的类型。例如,将其设置为一个字节可以节省一小部分空间,但根据CPU体系结构,可能需要更大、更慢的代码来访问它。
在C语言中,枚举常量的类型被定义为int类型。所以,给定以下内容:
enum foo { zero, one, two };
enum foo obj;

表达式zero的类型为int,但obj的类型为enum foo,可能与int的大小相同也可能不同。鉴于常量的类型为int,通常使枚举类型具有相同大小会更容易。

在C++中,规则不同; 常量是枚举类型。但出于效率原因,每个enum类型通常最好只有一个“字”,大小通常为int

而2011年的ISO C++标准增加了为enum类型指定底层整数类型的功能。例如,现在可以写成:

enum foo: unsigned char { zero, one, two };

该语言特性确保类型foo和常量zeroonetwo的大小为1字节。C语言没有这个特性,而且老版本的C++编译器也不支持它(除非它们提供了语言扩展功能)。

(接下来是插曲。)

那么如果您有一个枚举常量太大无法容纳在int中怎么办?您不需要231或甚至215个不同的常量来做到这一点:

#include <limits.h>
enum huge { big = INT_MAX, bigger };
big 的值为 INT_MAX,通常为 231-1,但最小可以是 215-1(32767)。 bigger 的值隐式为 big + 1
在 C++ 中,这是可以的;编译器将选择一个足够大以容纳 INT_MAX + 1 值的基础类型来表示 huge。(假设有这样的类型;如果 int 是64位且没有比它大的整数类型,那么这将不可能。)
在 C 中,由于枚举常量的类型是int,因此上述操作是无效的。 它违反了 N1570 6.7.2.2p2 中所述的限制:
“定义枚举常量值的表达式必须是一个可表示为 int 类型的整数常量表达式。”
因此编译器必须拒绝它或至少警告它。例如,gcc 显示以下错误信息:
“error: overflow in enumeration values”

10
枚举不是结构体,它只是一种为一组整数命名的方式。具有此类型的变量的大小只是底层整数类型的大小。这将是需要保存枚举中最大值的类型。因此,只要所有类型都适合同一整数类型,大小就不会改变。

我不认为 OP 认为枚举是结构体。你可能预期一个有 256 个元素的 enum 占用 8 位,而一个有 257 个元素的 enum 占用 16 位。但编译器通常会使所有的 enum 大小与一个 int 相同,因为那样可以更有效地访问。 - Keith Thompson
1
@KeithThompson 我理解原帖作者是将枚举看作结构体。事实上,他的评论提到他认为编译器会将枚举视为联合体。 - agbinfo

8
枚举类型的大小是由实现定义的,编译器可以选择任何大小,只要它足够大来容纳所有的值。一些编译器选择使用4字节枚举来表示所有枚举类型,而一些编译器将选择最小的类型(例如1、2或4字节),以适应枚举值。C和C++语言标准允许这两种行为。
从C99 §6.7.2.2/4中得知:
每个枚举类型都应与char、有符号整数类型或无符号整数类型兼容。类型的选择是由实现定义的,但必须能够表示枚举的所有成员的值。
从C++03 §7.2/5中得知:
枚举的底层类型是一个整数类型,可以表示枚举定义中定义的所有枚举器值。除非枚举器的值不能适合于int或unsigned int,否则使用哪种整数类型作为枚举的底层类型是由实现定义的。如果"enumerator-list"为空,则底层类型就像枚举具有单个值为0的枚举器一样。应用于枚举类型、枚举类型对象或枚举器的sizeof()的值是应用于底层类型的sizeof()的值。

好的回答,但有一件事我不明白,为什么 sizeof(enum) == sizeof(int) 而不是 sizeof(char)。有人不喜欢创建 2^sizeof(int) 个符号常量。 - Grijesh Chauhan
@GrijeshChauhan:sizeof(enum) 不一定等于 sizeof(int)。它可能是 sizeof(enum) == sizeof(some integral type),其中 some integral type 取决于枚举的 - Nawaz
如果枚举值都在 char 范围内,那么有可能会出现 sizeof(enum) == sizeof(char) 的情况。但是并不能保证这一点,完全取决于编译器的决定。 - Adam Rosenfield
sizeof(enum) == sizeof(int) 只在 C 语言中成立。对于 C++,其实现是未定义的。 - 0decimal0
4
在C语言中也不一定是这样。在C和C++中,enum类型的基础整型可以是实现定义的,它可以是charunsigned char,只要所有的常量都能够表示在该类型中。C语言枚举类型的常量int类型,但是类型本身并非如此。 - Keith Thompson

3

我觉得OP假设枚举是一种存储其中声明的值的集合,这是不正确的。

C/C++中的枚举只是一个具有严格定义值范围的数字变量。枚举名称是数字的一种别名。

枚举中的值数量不会影响其存储大小。存储大小由具体实现定义,但通常为sizeof(int)


1
enum的大小是“至少足够大以包含声明中指定的任何值的整数类型”。许多编译器将使用一个int(可能是unsigned),但有些会根据优化或其他因素使用charshort。具有少于128个可能值的enum将适合于charunsigned char为256),并且您必须拥有32768(或65536)个值才能溢出short,并且在大多数现代系统上,需要2或4十亿个值才能超越intenum本质上只是一种更好的定义一堆不同常量的方法。与此相比:
#define FIRST 0
#define SECOND 1
...

你刚刚做了什么:
enum myenum
{ FIRST,
  SECOND,
  ...
};

它有助于避免错误地分配重复值,并消除了您甚至需要关心特定值的需求(除非您真的需要)。

我有一个疑问,通过定义FIRST 0,它会分配任何内存吗? - anand
@jhakash:不,因为这不会创建一个对象;它只是一个编译时的定义。(顺便说一句,用“doubt”来表示“question”的意思在南亚方言的英语中更常见;在美国和英国的英语中,“doubt”往往暗示怀疑。“I have a question”会更清楚明了。) - Keith Thompson
@KeithThompson 哎呀,我不知道还有这个“doubt”的意思,谢谢你的澄清。 - anand

0
使用比int更小的枚举类型,当一个更小的类型可以容纳所有值时,会带来一个大问题,那就是它会使得翻译单元的ABI依赖于枚举常量的数量。例如,假设您有一个库,它使用了一个包含256个常量的枚举类型作为其公共接口的一部分,并且编译器选择将该类型表示为单个字节。现在假设您向库添加了一个新功能,现在需要257个常量。编译器必须切换到新的大小/表示方式,现在为旧接口编译的所有目标文件都与您更新的库不兼容;您必须重新编译所有内容才能使其再次正常工作。
因此,任何明智的实现都会始终使用int作为枚举类型。

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