【问题标题】:Is the sizeof(enum) == sizeof(int), always?sizeof(enum) == sizeof(int) 总是吗?
【发布时间】:2010-11-09 23:24:28
【问题描述】:

sizeof(enum) == sizeof(int),总是吗?

  • 还是依赖于编译器?
  • 这是错误的说法,因为编译器针对字长(内存对齐)进行了优化,即 y int 是特定编译器的字长?这是否意味着如果我使用枚举就没有处理惩罚,因为它们是字对齐的?
  • 如果我将所有返回码放在一个枚举中不是更好吗,因为我显然不担心它得到的值,只担心检查返回类型时的名称。如果是这种情况,#DEFINE 会不会更好,因为它会节省内存。

通常的做法是什么? 如果我必须通过网络传输这些返回类型并且必须在另一端进行一些处理,那么您更喜欢 enums/#defines/const ints。

编辑 - 只是在网上检查,因为编译器不会符号链接宏,那么人们如何调试,将整数值与头文件进行比较?

来自答案——我在下面添加这一行,因为我需要澄清——

“所以它是实现定义的,并且 sizeof(enum) 可能等于 sizeof(char),即 1。”

  • 这不是说编译器检查枚举值的范围,然后分配内存。我不这么认为,我当然不知道。谁能解释一下什么是“可能”。

【问题讨论】:

标签: c enums constants c-preprocessor


【解决方案1】:

它取决于编译器,并且在枚举之间可能会有所不同。以下是语义

enum X { A, B };

// A has type int
assert(sizeof(A) == sizeof(int));

// some integer type. Maybe even int. This is
// implementation defined. 
assert(sizeof(enum X) == sizeof(some_integer_type));

请注意,C99 中的“某些整数类型”也可能包括扩展整数类型(但是,如果提供了它们,实现必须记录下来)。枚举的类型是某种可以存储任何枚举数的值的类型(本例中为AB)。

我认为使用枚举没有任何惩罚。枚举器也是整数常量表达式(例如,您可以使用它来初始化静态或文件范围变量),我更喜欢它们而不是宏。

枚举器不需要任何运行时内存。只有在创建枚举类型的变量时,才能使用运行时内存。只需将枚举数视为编译时常量。

我将只使用一种可以存储枚举器值的类型(我应该事先知道值的粗略范围),转换为它,然后通过网络发送它。最好该类型应该是一些固定宽度的类型,例如int32_t,因此当涉及不同的机器时不会发生冲突。或者我会打印号码,然后在另一面扫描,这样可以解决其中的一些问题。


对编辑的回应

好吧,编译器不需要使用任何大小。很容易看出,值的符号很重要——无符号类型在某些计算中可以显着提升性能。以下是 GCC 4.4.0 在我的盒子上的行为

int main(void) {
  enum X { A = 0 };
  enum X a; // X compatible with "unsigned int"
  unsigned int *p = &a;
}

但是如果你分配一个-1,那么GCC选择使用int作为X兼容的类型

int main(void) {
  enum X { A = -1 };
  enum X a; // X compatible with "int"
  int *p = &a;
}

使用 GCC 的 --short-enums 选项,使其使用仍然适合所有值的最小类型。

int main() {
  enum X { A = 0 };
  enum X a; // X compatible with "unsigned char"
  unsigned char *p = &a;
}

在最新版本的 GCC 中,编译器标志已更改为 -fshort-enums。在某些目标上,默认类型是 unsigned int。您可以查看答案here

【讨论】:

  • 我现在认为,与其将每个命令存储在 unsigned char 变量中并将其等同于一个值,不如将其存储在 #define 中,就像它一直所做的那样。枚举将面临字节顺序问题。我想现在我明白为什么#define 一直被用于错误代码、状态名称、命令……等等。
  • 这并不完全正确,枚举和#defines 之间没有区别:正如您对#defines 所说,编译器甚至看不到初始标记,因为它被预处理器替换为实际值。编译器确实会看到枚举,如果您使用调试符号编译代码,调试器将显示枚举标签而不是它们的值,这极大地有助于调试。
  • 所有这些都是关于#define 的...如果其他人正在阅读这篇文章,那么请从大师那里得知,您最不想做的就是在 C++ 中使用#define 来处理常量/枚举。我没有在评论中添加使用方式和其他内容,但它不是#define's。
  • 在答案中,不清楚“以下是语义”是什么意思。您是否声称这些断言总是正确的?
【解决方案2】:
enum fruits {apple,orange,strawberry,grapefruit};  
char fruit = apple;  
fruit = orange;  
if (fruit < strawberry)  
...  

所有这些都完美无缺
如果您想要枚举实例的特定基础类型,请不要使用类型本身。

【讨论】:

    【解决方案3】:

    在某些编译器上,枚举的大小取决于枚举中有多少条目。 (少于 255 个条目 => 字节,超过 255 个条目 int) 但这取决于编译器和编译器设置。

    【讨论】:

    • 无论如何我可以强制执行此操作。一个很好的输入谢谢。
    • 由于我们项目中的这些问题(我们必须使用非常旧的 C 编译器),我们决定不使用枚举。但是用#define定义一切
    • 这与枚举成员的数量无关,而与它们所代表的值的范围有关。我可以有一个包含 2 个成员的 enum{apples = 0, oranges = 1000}。您认为编译器可以将它们放入任何char(“字节”)吗?并非没有枚举器值和基础值之间的映射,这显然不存在:这将是实现、性能和使用的噩梦。 enum 实际上只是一种具有某些上下文相关整数类型但能够使用文本标识符引用其成员的方法;成员被存储为他们的数值。
    【解决方案4】:

    没有。

    示例:The CodeSourcery compiler

    当你像这样定义一个枚举时:

    enum MyEnum1 {
    A=1,
    B=2,
    C=3
    };
    // will have the sizeof 1 (fits in a char)
    
    enum MyEnum1 {
    A=1,
    B=2,
    C=3,
    D=400
    };
    // will have the sizeof 2 (doesn't fit in a char)
    

    Details 来自他们的邮件列表

    【讨论】:

    【解决方案5】:

    是 sizeof(enum) == sizeof(int), 总是

    ANSI C 标准说:

    每个枚举类型都应与 char、有符号整数类型或无符号整数类型兼容。类型的选择是实现定义的。 (6.7.2.2 枚举说明符)

    所以我认为这意味着不。

    如果是这种情况,#DEFINE 会不会更好,因为它会节省内存。

    使用定义比使用枚举以什么方式节省内存?枚举只是一种允许您向编译器提供更多信息的类型。在实际生成的可执行文件中,它只是变成一个整数,就像预处理器将使用 #define 创建的宏转换为其值一样。

    通常的做法是什么。如果我必须通过网络传输这些返回类型并且必须在另一端进行一些处理

    如果您计划通过网络传输值并在另一端处理它们,您应该定义一个协议。确定每种类型的位大小、字节顺序(字节的顺序),并确保在客户端和服务器代码中都遵守这一点。也不要仅仅假设因为它碰巧起作用,你就做对了。例如,您选择的客户端和服务器平台上的字节序可能匹配,但情况并非总是如此。

    【讨论】:

    • 是的,这就是问题所在,我已经通过网络传输了一些值以用作命令,我希望尽可能高效和健壮,即你需要意见什么用于命令#定义或枚举,命令的范围不得超过20条命令,所以按all in char限制。我想我会把它作为一个新问题发布,我会得到更好的回应。
    • 最简单的事情就是简单地使用一个无符号字符。您不必担心字节顺序或编码方式。
    • 也许您可以为网络部分添加对Should I use cstdint? 的引用
    【解决方案6】:

    C99,6.7.2.2p4 说

    每个枚举类型应为 与 char 兼容,一个有符号的 整数类型,或无符号 整数类型。类型的选择 是实现定义的,108) 但是 应能够代表 所有成员的价值观 枚举。 [...]

    脚注 108 添加

    实现可能会延迟选择哪个整数 键入,直到看到所有枚举常量。

    所以它是实现定义的,sizeof(enum)可能等于sizeof(char),即1。

    在选择一些小范围整数的大小时,总是会受到惩罚。如果您将其缩小到内存中,则可能会有处理损失;如果你把它变大,就会有空间损失。这是一个时间和空间的权衡。

    错误代码通常是#defines,因为它们需要可扩展:不同的库可能会添加新的错误代码。枚举无法做到这一点。

    【讨论】:

    • 不是说编译器检查枚举值的范围,然后分配内存。我不这么认为,当然我不知道:)。有人可以解释一下什么是“可能”
    • “编译器会”是一个无用的语句。世界上有许多编译器,有些以一种方式执行,而另一些则以不同的方式执行(即使在相同的硬件上)。如果你想知道一个特定的编译器做什么,你必须命名编译器(包括版本和目标 CPU 和操作系统)。 你的编译器很可能总是使用 int 作为枚举。
    • 第二个答案here 给出了同一标准的不同版本,它说它必须与int 兼容。他的版本是过时的(他链接到草稿)还是你的?
    • @Norswap 我认为这个答案是对(相同)标准的误解。
    猜你喜欢
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 2013-09-19
    • 2013-10-20
    • 2013-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多