【问题标题】:Constant enum size no matter the number of enumerated values无论枚举值的数量如何,枚举大小都是恒定的
【发布时间】:2013-07-26 18:19:13
【问题描述】:

为什么enum 的大小总是 2 或 4 字节(分别在 16 位或 32 位架构上),而不管类型中的枚举数是多少?

编译器是否像对待union 一样对待enum

【问题讨论】:

  • @GrijeshChauhan:在 C 中是正确的,而不是在 C++ 中——在 C 中,枚举常量的大小可能与类型的大小不同。
  • 如果创建 2,147,483,648 个枚举会发生什么?我敢打赌编译器会胡思乱想......
  • @Jim 同样是我的疑问,不想创建超过 100 秒的符号常数。如果他这样做,他可能是个糟糕的程序员。
  • @Jim 开玩笑,如果你假设典型的大写字母 enum 标识符,并坚持统一长度,每个标识符必须是 7 个字符,加上一个逗号和空格,这意味着源文件本身大约为 20GB。我不希望很多编译器会特别乐意尝试通过它来咀嚼......
  • @LorenPechtel:enum too_big { big = INT_MAX, bigger };

标签: c++ c enums


【解决方案1】:

在 C 和 C++ 中,enum 类型的大小是实现定义的,并且与某些整数类型的大小相同。

一种常见的方法是使所有enum 类型的大小与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 没有此功能,并且旧的 2011 之前的 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++ 中,这是可以的;编译器将简单地为huge 选择一个大到足以容纳值INT_MAX + 1 的基础类型。 (假设有这样的类型;如果int 是 64 位,并且没有比这更大的整数类型,那是不可能的。)

在 C 中,由于枚举常量的类型为int,因此上述内容无效。它违反了N1570 6.7.2.2p2 中所述的约束:

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

因此编译器必须拒绝它,或者至少警告它。例如,gcc 说:

错误:枚举值溢出

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我不认为 OP 认为枚举是结构。你可能期望一个有 256 个元素的 enum 占据 8 位,而一个有 257 个元素的 enum 占据例如 16 位。但是编译器通常使所有enums 的大小与int 相同,只是因为这样可以提高访问效率。
    • @KeithThompson 我读到 OP 正在将枚举视为结构。事实上,他的评论提到他认为编译器会将枚举视为联合。
    【解决方案3】:

    枚举的大小是实现定义的——编译器可以选择它想要的任何大小,只要它大到足以容纳所有值。一些编译器选择对所有枚举类型使用 4 字节枚举,而一些编译器会选择适合枚举值的最小类型(例如 1、2 或 4 字节)。 C 和 C++ 语言标准允许这两种行为。

    来自 C99 §6.7.2.2/4:

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

    来自 C++03 §7.2/5:

    枚举的底层类型是一个整数类型,可以表示所有枚举数的值 在枚举中定义。使用哪种整数类型作为底层类型是实现定义的 用于枚举,但基础类型不得大于int,除非枚举器的值 不适合 intunsigned int。如果 enumerator-list 为空,则基础类型为 就好像枚举有一个值为 0 的枚举数。sizeof() 的值应用于枚举 type,一个枚举类型的对象,或者一个枚举器,是sizeof() 应用于 底层类型。

    【讨论】:

    • 很好的答案,但有一件事我不明白为什么sizeof(enum) == sizeof(int) 为什么不sizeof(char) 一个不喜欢创建2^sizeof(int) 符号常量
    • @GrijeshChauhan:sizeof(enum)不一定等于sizeof(int)。可能是sizeof(enum) == sizeof(some integral type),其中some integral type 取决于枚举的
    • @GrijeshChauhan:如果所有枚举值都在char 的范围内,那么有可能您将拥有sizeof(enum) == sizeof(char)。对此没有任何保证,但完全由编译器决定。
    • sizeof(enum) == sizeof(int) 仅适用于 C。对于 C++ ,其实现已定义。
    • @PHIfounder:C 也不一定如此。在 C 和 C++ 中,enum 类型的底层整数类型是实现定义的;它可以是charunsigned char,只要所有常量都可以在该类型中表示。 C 枚举 constants 的类型是 int,但类型本身不是。
    【解决方案4】:

    在我看来,OP 已经假设枚举是某种集合,它存储其中声明的值。这是不正确的。

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

    存储大小不受枚举值数量的影响。存储大小由实现定义,但主要是sizeof(int)

    【讨论】:

      【解决方案5】:

      enum 的大小是“一个至少大到足以包含声明中指定的任何值的整数类型”。许多编译器只会使用int(可能是unsigned),但有些编译器会使用charshort,这取决于优化或其他因素。具有少于 128 个可能值的 enum 将适合 charunsigned char 为 256),并且您必须有 32768(或 65536)个值才能溢出 short,以及 2 或 40 亿在大多数现代系统上,价值超过int

      enum 本质上只是定义一堆不同常量的更好方法。而不是这个:

      #define FIRST 0
      #define SECOND 1
      ...
      

      你只是:

      enum myenum
      { FIRST,
        SECOND,
        ...
      };
      

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

      【讨论】:

      • 我有一个疑问,通过定义FIRST 0,它是否分配了任何内存?
      • @jhakash:不,因为这不会创建对象;它只是一个编译时定义。 (顺便说一句,使用“doubt”来表示“question”的意思是特定于南亚英语方言的时态;在美国和英国英语中,“doubt”往往意味着不相信。“I have a question”会更清楚。
      • @KeithThompson 哎呀。我也不知道怀疑的事情,也感谢您的澄清。
      【解决方案6】:

      当较小的类型可以适合所有值时,使enum 类型小于int 的大问题是它会使翻译单元的ABI 依赖于枚举常量的数量。例如,假设您有一个库使用具有 256 个常量的 enum 类型作为其公共接口的一部分,并且编译器选择将类型表示为单个字节。现在假设您向库中添加了一个新功能,现在需要 257 个常量。编译器必须切换到新的大小/表示,现在为旧接口编译的所有目标文件都将与您更新的库不兼容;您必须重新编译所有内容才能使其再次工作。

      因此,任何理智的实现总是将int 用于enum 类型。

      【讨论】:

        猜你喜欢
        • 2014-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-12
        相关资源
        最近更新 更多