在C中指定枚举类型的大小

Wil*_*ill 30 c optimization enums word-size

已经阅读了这个相关的问题,但正在寻找一些更具体的东西.

  • 有没有办法告诉你的编译器具体你想要你的枚举有多宽?
  • 如果是这样,你怎么做?我知道如何在C#中指定它; 它在C中同样完成了吗?
  • 它甚至值得做吗?当枚举值传递给函数时,是否会将其作为int-sized值传递?

Nyx*_*0uf 21

如果您使用GCC,我相信会有一面旗帜.

-fshort,枚举

  • 或者有选择地`enum __attribute __((__ package__))my_enum {...};` (15认同)
  • @Nikolai N Fetissov:也会尝试一下。我知道你可以在 `struct` 上使用 `__packed__` 属性;不知道你也可以在`enum` 上使用它。 (3认同)

AnT*_*AnT 18

有没有办法告诉你的编译器具体你想要你的枚举有多宽?

一般情况下没有.不在标准C.

它甚至值得做吗?

这取决于具体情况.如果你在谈论将函数传递给函数,那么不值得做(见下文).如果它是在从枚举类型构建聚合时节省内存,那么它可能值得做.但是,在C中,您只需在聚合中使用适当大小的整数类型而不是枚举类型.在C(而不是C++)中,枚举类型和整数类型几乎总是可以互换的.

当枚举值传递给函数时,它是否会作为int大小的值传递?

如今,许多(大多数)编译器将所有参数作为给定硬件平台的自然字大小的值传递.例如,在64位平台上,许多编译器将所有参数作为64位值传递,而不管它们的实际大小如何,即使类型int在该平台上有32位(因此,它通常不会作为"int-大小"在这样的平台上的价值".因此,尝试针对参数传递目的优化枚举大小是没有意义的.

  • “如果您正在谈论将参数传递给函数,那么不,不值得这样做(见下文)”并不总是如此。如果您使用 8 位处理器,自然大小为 8 位,但 int 和 enum 至少为 16 位(没有 -fshort-enums),因此您至少需要 2 个寄存器,这会减慢函数调用的速度。 (4认同)

Tau*_*Tau 13

从 C23 开始,这在标准 C 中终于成为可能:

enum您可以在关键字后面(或在名称标签后面,如果已命名)放置冒号和整数类型来指定枚举的固定底层类型,该类型设置枚举类型的大小和范围。

这值得吗?当枚举值传递给函数时,它是否会作为 int 大小的值传递?

在 x86_64 上,整数的类型不会影响它是否在寄存器中传递(只要它适合单个寄存器)。然而,堆上数据的大小对于缓存性能非常重要。

  • 参考:https://en.cppreference.com/w/c/language/enum (3认同)

Kri*_*son 8

您可以通过定义适当的值来强制它至少达到一定的大小.例如,如果您希望将枚举存储为与a相同的大小int,即使所有值都适合a char,您也可以执行以下操作:

typedef enum {
    firstValue = 1,
    secondValue = 2,

    Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;
Run Code Online (Sandbox Code Playgroud)

但请注意,行为可能取决于实现.

正如您所注意的那样,将这样的值传递给函数将导致它无论如何都会扩展为int,但如果您在数组或结构中使用类型,则大小将很重要.如果你真的关心元素的大小,你应该使用类型,如int8_t,int32_t等.


Jul*_*ien 8

在某些情况下,这可能会有所帮助:

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; 
Run Code Online (Sandbox Code Playgroud)

一方面,类型的command_t大小为 8,可用于变量和函数参数类型。另一方面,您可以使用int默认类型的枚举值进行赋值,但编译器将在分配给command_t类型变量时立即强制转换它们。

另外,如果你做了一些不安全的事情,比如定义和使用 aCMD_16bit = 0xFFFF,编译器会用以下消息警告你:

警告:大整数隐式截断为无符号类型 [-Woverflow]


Pat*_*ter 7

如果枚举是结构的一部分,还有另一种方法:

struct something {
     :0;
   enum whatever field:CHAR_BIT;
     :0;
};
Run Code Online (Sandbox Code Playgroud)

:0; 如果枚举字段被普通字段包围,则可以省略.如果之前有另一个位域,则:0将强制字节对齐到其后的字段的下一个字节.

  • 这是标准C吗? (4认同)
  • C11 6.7.2.1p5 表示 _位字段应具有 _Bool、signed int、unsigned int 或其他一些实现定义的类型的限定或非限定版本的类型_,这意味着您的答案仅在实现允许的情况下才有效枚举作为位字段类型。C11 6.7.2.1p11 表示_实现可以分配任何足够大以容纳位字段的可寻址存储单元_,它不必是基础类型的倍数(我之前假设过)。 (2认同)

Gab*_*les 5

正如 @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
有趣...请注意下面我用黄色突出显示的行!添加一个名为 and 的枚举条目,其ARM_EXCEPTION_MAKE_ENUM_32_BIT值等于0xffffffff,这相当于UINT32_MAXfrom stdint.h(请参见此处此处),强制此特定Arm_symbolic_exception_name枚举具有整数类型uint32_t。这就是此条目的唯一目的ARM_EXCEPTION_MAKE_ENUM_32_BIT它之所以有效,uint32_t是因为 是可以包含此枚举中所有枚举值的最小整数类型,即:0through 8、包容性以及0xffffffff、 或decimal 2^32-1= 4294967295

在此输入图像描述

关键字:ARM_EXCEPTION_MAKE_ENUM_32_BIT enum 目的为什么要有它?Arm_symbolic_exception_name 结尾处 0xffffffff 枚举条目的用途。


Zak*_*Zak 5

即使您正在编写严格的C代码,结果也将取决于编译器。使用这个线程中的策略,我得到了一些有趣的结果......

enum_size.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;
}
Run Code Online (Sandbox Code Playgroud)
$ gcc enum_size.c
$ ./a.out
packed:         4
unpacked:       4
Run Code Online (Sandbox Code Playgroud)
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed:         4
unpacked:       4
Run Code Online (Sandbox Code Playgroud)
$ g++ enum_size.c
$ ./a.out
packed:         1
unpacked:       4
Run Code Online (Sandbox Code Playgroud)
$ g++ enum_size.c -fshort_enums
$ ./a.out
packed:         1
unpacked:       1
Run Code Online (Sandbox Code Playgroud)

在我上面的例子中,__attribute__((__packed__))直到我开始使用 C++ 编译器,我才意识到修饰符的任何好处。

编辑:

@technosaurus 的怀疑是正确的。

通过检查大小sizeof(enum PackedFlags)而不是sizeof(PACKED)我看到了我预期的结果......

$ gcc enum_size.c
$ ./a.out
packed:         4
unpacked:       4
Run Code Online (Sandbox Code Playgroud)

我现在看到了来自gcc以下方面的预期结果:

$ gcc enum_size.c
$ ./a.out
packed:         1
unpacked:       4
Run Code Online (Sandbox Code Playgroud)
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed:         1
unpacked:       1
Run Code Online (Sandbox Code Playgroud)

  • 尝试使用“sizeof (enum PackedFlags)”,这样枚举就不会被扩展。您对 sizeof( 'a' ) 会有类似的体验 (3认同)