已经阅读了这个相关的问题,但正在寻找更具体的东西。
- 有没有办法具体告诉你的编译器你希望你的枚举有多宽?
- 如果是这样,你怎么做?我知道如何在 C# 中指定它;在 C 中是否类似地完成?
- 它甚至值得做吗?当枚举值传递给函数时,它是否会作为
int
-sized 值传递?
已经阅读了这个相关的问题,但正在寻找更具体的东西。
int
-sized 值传递?如果您使用 GCC,我相信会有一个标志。
-fshort-enums
有没有办法具体告诉你的编译器你希望你的枚举有多宽?
一般情况下没有。不在标准 C 中。
它甚至值得做吗?
这取决于上下文。如果您正在谈论将参数传递给函数,那么不,不值得这样做(见下文)。如果它是关于在从枚举类型构建聚合时节省内存,那么它可能是值得的。但是,在 C 中,您可以简单地在聚合中使用适当大小的整数类型而不是枚举类型。在 C(相对于 C++)中,枚举类型和整数类型几乎总是可以互换的。
将枚举值传递给函数时,无论如何都会将其作为 int 大小的值传递吗?
如今,许多(大多数)编译器将所有参数作为给定硬件平台的自然字长值传递。例如,在 64 位平台上,许多编译器将所有参数作为 64 位值传递,而不管它们的实际大小,即使int
在该平台上 type 有 32 位(因此,它通常不会作为“int-在这样一个平台上的“大小”值)。出于这个原因,尝试优化枚举大小以用于参数传递是没有意义的。
您可以通过定义适当的值来强制它至少具有一定的大小。例如,如果您希望 enum 存储为与 a 相同的大小int
,即使所有值都适合 a char
,您可以执行以下操作:
typedef enum {
firstValue = 1,
secondValue = 2,
Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;
但是请注意,行为可能取决于实现。
正如您所注意到的,将这样的值传递给函数将导致它被扩展为 int 无论如何,但如果您在数组或结构中使用您的类型,那么大小将很重要。如果您真的关心元素大小,您应该真正使用int8_t
,int32_t
等类型。
在某些情况下,这可能会有所帮助:
typedef uint8_t command_t;
enum command_enum
{
CMD_IDENT = 0x00, //!< Identify command
CMD_SCENE_0 = 0x10, //!< Recall Scene 0 command
CMD_SCENE_1 = 0x11, //!< Recall Scene 1 command
CMD_SCENE_2 = 0x12, //!< Recall Scene 2 command
};
/* cmdVariable is of size 8 */
command_t cmdVariable = CMD_IDENT;
一方面类型command_t
的大小为 8,可用于变量和函数参数类型。另一方面,您可以使用int
默认类型的枚举值进行赋值,但编译器将在分配给command_t
类型变量时立即转换它们。
此外,如果你做了一些不安全的事情,比如定义和使用CMD_16bit = 0xFFFF,
编译器,编译器会警告你以下消息:
警告:大整数隐式截断为无符号类型 [-Woverflow]
如果枚举是结构的一部分,还有另一种方法:
struct something {
:0;
enum whatever field:CHAR_BIT;
:0;
};
:0; 如果枚举字段被普通字段包围,则可以省略。如果之前有另一个位域, :0 将强制字节对齐到它后面的字段的下一个字节。
即使您正在编写严格C
的代码,结果也将取决于编译器。使用这个线程中的策略,我得到了一些有趣的结果......
枚举大小.c
#include <stdio.h>
enum __attribute__((__packed__)) PackedFlags {
PACKED = 0b00000001,
};
enum UnpackedFlags {
UNPACKED = 0b00000001,
};
int main (int argc, char * argv[]) {
printf("packed:\t\t%lu\n", sizeof(PACKED));
printf("unpacked:\t%lu\n", sizeof(UNPACKED));
return 0;
}
$ gcc enum_size.c
$ ./a.out
packed: 4
unpacked: 4
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed: 4
unpacked: 4
$ g++ enum_size.c
$ ./a.out
packed: 1
unpacked: 4
$ g++ enum_size.c -fshort_enums
$ ./a.out
packed: 1
unpacked: 1
__attribute__((__packed__))
在上面的示例中,直到我开始使用 C++ 编译器,我才意识到修饰符有任何好处。
编辑:
@technosaurus 的怀疑是正确的。
通过检查大小sizeof(enum PackedFlags)
而不是sizeof(PACKED)
我看到了我预期的结果......
printf("packed:\t\t%lu\n", sizeof(enum PackedFlags));
printf("unpacked:\t%lu\n", sizeof(enum UnpackedFlags));
我现在看到了预期的结果gcc
:
$ gcc enum_size.c
$ ./a.out
packed: 1
unpacked: 4
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed: 1
unpacked: 1
正如@Nyx0uf所说,GCC 有一个可以设置的标志:
-fshort-enums
分配给枚举类型的字节数仅与声明的可能值范围所需的字节数一样多。具体来说,枚举类型相当于有足够空间的最小整数类型。
警告:该
-fshort-enums
开关导致 GCC 生成的代码与没有该开关生成的代码二进制不兼容。使用它来符合非默认应用程序二进制接口。
来源:https ://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
更多关于一般见解的精彩阅读: https ://www.embedded.fm/blog/2016/6/28/how-big-is-an-enum 。
有趣...请注意我在下面以黄色突出显示的行!添加一个名为ARM_EXCEPTION_MAKE_ENUM_32_BIT
and 的枚举条目,其值等于0xffffffff
,相当于UINT32_MAX
from stdint.h
(参见此处和此处),强制此特定Arm_symbolic_exception_name
枚举具有整数类型uint32_t
。这是ARM_EXCEPTION_MAKE_ENUM_32_BIT
此条目的唯一目的!它之所以有效,是因为它uint32_t
是最小的整数类型,它可以包含此枚举中的所有枚举值——即:0
通过8
、包含、以及0xffffffff
、或小数2^32-1
= 4294967295
。
关键词:ARM_EXCEPTION_MAKE_ENUM_32_BIT enum 目的为什么要有呢?Arm_symbolic_exception_name 末尾的 0xffffffff 枚举条目的用途。
它取决于为枚举分配的值。
例如:如果存储了大于 2^32-1 的值,则分配给整个枚举的大小将更改为下一个大小。
将 0xFFFFFFFFFFFF 值存储到枚举变量中,如果尝试在 32 位环境中编译会发出警告(舍入警告),而在 64 位编译中会成功,分配的大小为 8 字节。
另一种方法是将枚举投射到联合中,如下所示:
union char_size {
char a;
enum {
a = 1,
b = 2,
} val;
};
这样做会强制编译器将枚举放入字符中。