强制结构中枚举字段的大小
[注意:这与指定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_type
和voltage
只能采用某些值的事实。
我更喜欢这样的东西……
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'