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

时间:2011-02-02 20:05:20

标签: c optimization enums word-size

已经通过this related question阅读,但正在寻找更具体的内容。

  • 有没有办法告诉你的编译器你想要你的枚举有多宽?
  • 如果是这样,你是怎么做到的?我知道如何在C#中指定它;是否在C?中类似地完成了?
  • 它甚至值得做吗?将枚举值传递给函数时,它是否会以int大小的值传递?

9 个答案:

答案 0 :(得分:18)

  

有没有办法告诉你的编译器   特别是你想要的宽度   枚举是什么?

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

  

它甚至值得做吗?

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

  

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

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

答案 1 :(得分:15)

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

  

-fshort-枚举

答案 2 :(得分:8)

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

typedef enum {
    firstValue = 1,
    secondValue = 2,

    Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;

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

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

答案 3 :(得分:7)

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

struct something {
     :0;
   enum whatever field:CHAR_BIT;
     :0;
};

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

答案 4 :(得分:0)

取决于为枚举分配的值。

例: 如果存储的值大于2 ^ 32-1,则为整个枚举分配的大小将更改为下一个大小。

将0xFFFFFFFFFFFF值存储到枚举变量,如果尝试在32位环境中编译,它将发出警告(舍入警告) 在64位编译中,它将成功,分配的大小将为8个字节。

答案 5 :(得分:0)

与@ Nyx0uf says一样,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
有趣的是...注意我下面用黄色突出显示的行!

enter image description here

答案 6 :(得分:0)

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

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]

答案 7 :(得分:0)

即使您正在编写严格的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;
}
$ 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

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

编辑:

@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

答案 8 :(得分:-16)

另一种方法是将联合体内的枚举转换为如下:

union char_size {
  char a;
  enum {
    a = 1,
    b = 2,
  } val;
};

这样做会强制编译器在枚举中使用枚举。