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

60

枚举类型的大小是否总是等于整型的大小?

  • 还是这取决于编译器?
  • 如果编译器针对字长(内存对齐)进行优化,例如int在特定编译器上是字长,那么说“使用枚举不会产生处理惩罚,因为它们将按字长对齐”是否正确?
  • 如果我把所有返回代码放入一个枚举中,是否更好,因为我在检查返回类型时只关心名称而不关心值。如果是这种情况,#DEFINE 是否更好,因为它可以节省内存。

通常的做法是什么? 如果我必须在网络上传输这些返回类型并且需要在另一端进行一些处理,您会更喜欢使用枚举/ #define / const int。

编辑-在网上查找时,由于编译器不会符号链接宏,人们如何进行调试,将整数值与头文件进行比较吗?

从答案中添加以下内容 - 我添加了下面这行文字以寻求澄清 -

“因此,它是实现定义的,sizeof(enum)可能等于sizeof(char),即1。”

  • 这不意味着编译器检查枚举值的范围,然后分配内存吗?我不这么认为,当然我也不知道。有人可以请解释一下“可能是”什么意思吗?

请查看以下链接:https://dev59.com/ZXRC5IYBdhLWcg3wP-dh还有这个链接:http://bytes.com/groups/cpp/135139-sizeof-enum-sizeof-int - Macarse
在某个论坛上,有人说:“我认为枚举应该尽可能小,以容纳其所有的值,在这种情况下为1字节。” 这是真的吗? - Vivek Sharma
感谢大家,我正在学习一些合理的疑问,我相信这些疑问一定会帮助我更好地理解概念。 - Vivek Sharma
我发现来自“nuriaion”和“lulian Sebaniou”的答案与问题的一部分相关。请问有人能否评论其正确性。 - Vivek Sharma
1
可能是在C语言中枚举的大小是多少?的重复问题。 - underscore_d
6个回答

36

这是编译器相关的,可能在枚举类型之间有所不同。以下是语义。

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。您可以在此处查看答案


我认为现在,与其将每个命令存储在无符号字符变量中并将其等同于一个值,不如将其存储在 #define 中,因为这一直是这样做的。枚举将面临字节顺序问题。我现在明白为什么 #define 一直被用于错误代码、状态名称、命令等。 - Vivek Sharma
11
枚举和#defines并不完全相同:就像你所说,对于#defines,编译器甚至看不到初始标记,因为它被预处理程序替换成实际值。然而,编译器确实可以看到枚举,如果使用调试符号编译代码,则调试器将显示枚举标签而不是其值,这对于调试非常有帮助。 - Metiu
1
所有这些关于 #define 的讨论... 如果还有其他人在阅读这篇文章,那么请相信一位专家的建议:在 C++ 中,你最不想做的事情就是使用 #define 来定义常量/枚举。我不会在评论中添加如何以及使用什么来代替 #define,但它绝对不是 #define。 - Carlo Wood
1
在这段内容中,"the following are the semantics" 的含义不太清楚。您是在说这些断言总是成立吗? - xdavidliu

18

C99, 6.7.2.2p4规定

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

脚注108补充说

一个实现可以推迟选择整数类型,直到看到所有枚举常量为止。

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

在选择一些小范围整数的大小时,总会有一些代价。如果你将其缩小到内存中,可能会有处理代价;如果你将其扩大,就会有空间代价。这是时间和空间的权衡。

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


这难道不意味着编译器会检查枚举值的范围,然后分配内存吗?我不这么认为,当然我也不确定 : )。请问有人能解释一下什么是“可能是”吗? - Vivek Sharma
“编译器会”这样的陈述是无用的。世界上有许多编译器,有些以一种方式执行,而其他编译器则以不同的方式执行(即使在相同的硬件上)。如果您想知道特定编译器的操作方式,必须命名该编译器(包括版本、目标CPU和操作系统)。很可能您的编译器总是将int用于枚举类型。 - Martin v. Löwis
这里的第二个回答给出了一个不同版本的相同标准,其中它必须与“int”兼容。他的版本已过时(他链接到了一份草案)还是你的版本? - Norswap
1
@Norswap 我认为那个答案是对(同一)标准的错误解释。 - user824425

13

sizeof(enum)是否始终等于sizeof(int)

ANSI C标准规定:

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

所以我认为不一定相等。

如果是这种情况,使用#define会更好,因为它可以节省内存。

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

常规做法是什么,如果我必须在网络上传输这些返回类型,并且其他端需要进行处理?

如果您计划在网络上传输值并在另一端进行处理,则应定义协议。确定每个类型的位大小、字节顺序(字节的顺序)并确保在客户端代码和服务器代码中均遵守该协议。同时不要假设因为它恰好起作用所以你就做对了。例如,在所选的客户端和服务器平台上匹配字节顺序可能不总是成立。


是的,这是我的担忧。我已经传输了一些值以用作网络命令,并希望尽可能地使其高效和稳健。也就是说,我需要意见,应该使用#define还是枚举来进行命令控制。命令范围不会超过20个,因此需要考虑字符限制。我认为我应该将其发布为一个新问题,这样我可以得到更好的回答。 - Vivek Sharma
1
最简单的方法就是直接使用无符号字符。这样你就不必担心字节序或编码的问题了。 - IRBMe
也许你可以在网络部分添加一个对于“我应该使用cstdint吗?”(Should I use cstdint?)的参考链接:https://dev59.com/92025IYBdhLWcg3wOzE9 - Wolf

5

不。

示例:CodeSourcery编译器

当您定义枚举时,可以这样做:

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)

这里是他们邮件列表中的详细信息。


如果是这样的话,那真是太棒了。 - Vivek Sharma
4
不是真的,这取决于实现。在我的系统上,两个代码的输出都是4。平台为Ubuntu 10.04(32位),gcc编译器。 - Amol Sharma
这不是一个坏的例子,但是两个链接http://www.codesourcery.com/sgpp(第一个)和http://www.codesourcery.com/archives/arm-gnu/msg02684.html(第二个)都已经失效。(希望能有修复更新。) - Wolf

2

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


有没有任何方法可以强制实现这个。虽然谢谢您提供的输入。 - Vivek Sharma
由于我们的项目存在这些问题(我们必须使用非常老的C编译器),因此我们决定不使用枚举,而是用#define定义所有内容。 - nuriaion
重点不在于枚举成员的数量,而在于它们所代表的值的范围。我可以有一个包含2个成员的enum{apples = 0, oranges = 1000}。你认为编译器能将它们适配到任何char(“字节”)中吗?显然不行,因为需要在枚举值和底层值之间建立映射,这显然是实现、性能和使用方面的噩梦。enum实际上只是一种具有某些上下文敏感整数类型的方式,但可以使用文本标识符引用其成员;成员存储为它们的数字值。 - underscore_d

0
enum fruits {apple,orange,strawberry,grapefruit};  
char fruit = apple;  
fruit = orange;  
if (fruit < strawberry)  
...  

所有这些都完美地工作
如果您想要枚举实例的特定基础类型,只需不使用类型本身即可。


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