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

toiithl6  于 2022-12-03  发布在  其他
关注(0)|答案(9)|浏览(147)

已经通读了this related question,但正在寻找更具体一点的东西。

  • 有没有办法告诉你的编译器你希望你的枚举有多宽?
  • 如果是的话,你是怎么做到的?我知道如何用C#指定它;在C语言中也是这样吗?
  • 这样做值得吗?当枚举值传递给函数时,它会作为int大小的值传递吗?
x4shl7ld

x4shl7ld1#

我相信如果你使用愚者的话会有一个标志。

  • f短枚举
xbp102n0

xbp102n02#

有没有办法告诉你的编译器你希望你的枚举有多宽?
一般情况下,不属于标准C。
这值得吗?
这取决于上下文。如果你在谈论传递参数给函数,那么不,这不值得做如果它是关于在从枚举类型构建聚合时节省内存,那么它可能是值得做的。但是,在C语言中,你可以简单地使用一个适当大小的整数类型来代替集合中的枚举类型。(与C++相反)枚举类型和整数类型几乎总是可以互换的。
当枚举值被传递给函数时,它是否会作为整数大小的值传递?
许多现在,大多数编译器将所有参数作为给定硬件平台的自然字长值传递。例如,在64位平台上,许多编译器将所有参数作为64位值传递,而不管它们的实际大小,即使int类型在该平台上有32位(因此,在这样的平台上,它通常不作为“int-sized”值传递)。因此,为参数传递目的而尝试优化枚举大小是没有意义的。

xmq68pz9

xmq68pz93#

你可以定义一个合适的值来强制它至少有一定的大小。例如,如果你想让你的枚举存储为与int相同的大小,即使所有的值都适合一个char,你可以这样做:

typedef enum {
    firstValue = 1,
    secondValue = 2,

    Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;

但是,请注意,行为可能取决于实现。
正如您所注意到的,将这样的值传递给函数会导致它被扩展为int类型,但是如果您在数组或结构体中使用您的类型,那么大小就很重要了。如果您真的关心元素大小,那么您真的应该使用int8_tint32_t等类型。

46scxncf

46scxncf4#

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

enum whatever { a,b,c,d };

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

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

8ljdwjyq

8ljdwjyq5#

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

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的大小为1(8位),可用于变量和函数参数类型。另一方面,您可以使用默认为int类型的枚举值进行赋值,但当将其赋值给command_t类型变量时,编译器会立即对其进行强制转换。
此外,如果您做了一些不安全的事情,例如定义和使用CMD_16bit = 0xFFFF,,编译器将通过以下消息警告您:
警告:大整数隐式截断为无符号类型[-Woverflow]

2nc8po8w

2nc8po8w6#

即使你正在写严格的C代码,结果也将依赖于编译器。

枚举大小.c

第一个
在我上面的例子中,直到我开始使用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的预期结果:
第一个

krugob8w

krugob8w7#

作为@Nyx0uf says,愚者有一个可以设置的标志:
-fshort-enums
仅向枚举类型分配可能值的声明范围所需的字节数。特别是,枚举类型等效于具有足够空间的最小整数类型。
警告:-fshort-enums开关导致愚者生成的代码与不使用该开关生成的代码不兼容。请使用该开关以符合非默认的应用程序二进制接口。
来源: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的枚举条目,其值等于0xffffffff,它相当于stdint.h中的UINT32_MAX(参见herehere),强制这个Arm_symbolic_exception_name枚举具有uint32_t的整数类型。这是这个ARM_EXCEPTION_MAKE_ENUM_32_BIT条目的唯一用途!* 它之所以有效,是因为uint32_t是可以包含此枚举中所有枚举值的最小整数类型--即:08(含)以及0xffffffff,或十进制2^32-1 = 4294967295。*

关键词:ARM_EXCEPTION_MAKE_ENUM_32_短距枚举目的为什么有它?Arm_symbolic_exception_name 0xffffffff枚举目的条目在末尾。

tjrkku2a

tjrkku2a8#

现在我不能回答你的前两个问题,因为我自己也在尝试找到一个好的方法来做这件事。如果我找到了一个我喜欢的策略,也许我会修改它。尽管它不是直观的。
但我想指出一些到目前为止还没有提到的东西,为此,我将这样回答第三个问题:

当编写一个将从非C语言调用的C API时,这是“值得做的”。任何直接链接到C代码的东西都需要正确理解C代码API中所有结构、参数列表等的内存布局。不幸的是,像int这样的C类型,或者更糟糕的是,枚举,其大小是相当不可预测的(由编译器、平台等更改),因此,了解包含枚举的任何内容的内存布局都可能是不可靠的,除非您的其他编程语言的编译器也是C编译器,并且它具有某种语言内机制来利用这些知识。大小可预测的C类型,如uint8_tuint16_tuint32_tuint64_tvoid*uintptr_t等,以及由这些大小可预测的类型组成的结构/联合。

因此,当枚举大小对程序正确性很重要时,比如可能存在内存布局和对齐问题时,我会关心枚举大小。但对于优化,我不会太担心它,除非您有一些放大机会成本的利基情况(例如:内存受限系统(如小型MCU)上枚举类型值的大型数组/列表)。
不幸的是,-fshort-enums之类的功能对我所提到的情况没有帮助,因为此功能是特定于供应商的,并且 * 较难 * 预测(例如,另一系统将不得不通过近似愚者的用于-fshort-enums枚举大小调整的算法来“猜测”枚举大小)。如果有的话,它将允许人们以一种打破其他语言中绑定所做的常见假设的方式来编译C代码(或其他未使用相同选项编译的C程式码),预期的结果是内存损毁,因为参数或结构成员写入或读取内存中的错误位置。

hvvq6cgz

hvvq6cgz9#

它取决于为枚举分配的值。
例如:如果存储大于2^32-1的值,则为整个枚举分配的大小将更改为下一个大小。
将0xFFFFFFFFFF值存储到一个枚举变量中,如果尝试在32位环境中编译,它将给予警告(舍入警告)。在64位环境中,编译将成功,分配的大小为8字节。

相关问题