强制结构中枚举字段的大小

[注意:这与指定C中枚举类型 的大小和C中枚举的大小有关? ,但在这些问题中,问题是如何最小化封闭结构的大小。 在这里,我们要指定结构的各个成员的大小,但仍然获得typedef’d enums的文档优势]

我正在实现串行通信协议的客户端。 我想使用C结构来捕获串行数据包的格式,我想使用C枚举来定义数据包中各种插槽的合法值。

我没有办法做到这两点,但由于我使用GCC,它可能是可能的。

作为问题的假设示例,假设我收到的数据包如下所示:

typedef struct { uint16_t appliance_type; uint8_t voltage; uint16_t crc; } __attribute__((__packed__)) appliance_t; 

这很清楚,我很确定我会得到一个长度为五个字节的结构。 但它没有捕获appliance_typevoltage只能采用某些值的事实。

我更喜欢这样的东西……

 typedef enum { kRefrigerator = 600, kToaster = 700, kBlender = 800 } appliance_type_t; typedef enum { k120vac = 0, k240vac = 1, k12vdc = 2 } voltage_t; typedef struct { appliance_type_t appliance_type; voltage_t voltage; uint16_t crc; } appliance_t; 

…但我不知道指定appliance_type_t是16位且voltage_t是8位。

有没有办法吃我的蛋糕也吃它?

更新:

我需要明确指出,我并不期望编译器将enum’d值强制为各个字段的setter! 相反,我发现typedef’d枚举是代码维护者的有用构造,因为它明确了预期值是什么。

注意:当我研究这个时,我注意到GCC枚举接受__attribute__规范,但我不确定这有帮助。

我建议你将“原始数据”与抽象分开。 例如:

 typedef struct { uint16_t type; uint8_t voltage; uint16_t crc; } __attribute__((__packed__)) raw_data_t; typedef struct { appliance_type_t type; voltage_t voltage; uint16_t crc; } appliance_t; inline appliance_t raw_data_to_appliance (const raw_data_t* raw) { appliance_t app = { .type = (appliance_type_t)raw->type, .voltage = (voltage_t)raw->voltage, .crc = raw->crc, }; return app; } 

这不应该产生很多开销代码。 另请参见如何创建类型安全枚举?

这样做的一种方法是使用位字段和结构中成员的定义。

所以你的结构将是 –

 typedef struct { appliance_type_t appliance_type:16; voltage_t voltage:8; uint16_t crc; } appliance_t; 

但是这将在电压场之后留下填充(虽然取决于编译器的实现)。 packed属性可以帮助你。

所以我理解,当你声明一个枚举时,你无法控制它的大小

 typedef struct { appliance_type_t appliance_type; voltage_t voltage; uint16_t crc; } appliance_t; 

你想要例如voltage_t voltage是一个特定的大小,让我们说uint8_t但你无法指定。

我不知道你是否可以在枚举上提供大小限制,但是你可以使用两种机制。 在这两种机制中,您指定unit8_t voltage作为结构的成员:

 typedef struct { uint8_t appliance_type; uint8_t voltage; uint16_t crc; } appliance_t; 

方法1使用普通的#define 。 您只需#define值并在赋值中使用符号常量:

 #define k120vac 0 appliance.voltage= k120vac; 

在方法2中,您有枚举,但仍然声明了您想要的大小的成员。 现在您可以将枚举值分配给成员,例如:

 appliance.voltage= k120vac; 

注意:我运行了简单测试(VC2008)并且还使用了voltage_t voltage; 声明,你可以做appliance.voltage= kToaster; 所以甚至没有给编译器“枚举类型安全”(仅检查来自域的值被分配/使用),所以即使已经丢失了我所展示的2种方法。


做了一些测试:

 unsigned char x = k12vdc; // <== OK unsigned char y = kToaster; // <== warning: truncation of int to unsigned char 

因此,编译器在分配时似乎使用可能的最小类型的枚举值。

似乎没有与枚举本身相关的大小,只有它的个别值。 现在重新定义k12vdc= 2000也会对赋值x= k12vdc发出警告,但不会对x=k120vac发出警告。

现在,鉴于编译器(VC2008)不强制枚举的类型安全性,并且您无法控制变量或枚举类型成员的大小,因此在声明变量或枚举类型成员时似乎没有用处,除了文件目的。 枚举类型的变量/成员将是int的大小,如以下示例所示:

 appliance_type_t a; voltage_t v; printf("sizeof appliance_type_t= %d\n", sizeof(a)); // prints '4' printf("sizeof voltage_t= %d\n", sizeof(v)); // prints '4'