[注意:这与Specifying size of enum type in C和What is the size of an enum in 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__
规范,但我不确定是否有帮助。
答案 0 :(得分:0)
这样做的一种方法是使用位字段和结构中成员的定义。
所以你的结构将是 -
typedef struct {
appliance_type_t appliance_type:16;
voltage_t voltage:8;
uint16_t crc;
} appliance_t;
但这会在电压场之后留下填充(虽然取决于编译器的实现)。 packed属性可以帮助你。
答案 1 :(得分:0)
我建议您保留"原始数据"与抽象分开。例如:
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;
}
这不应该产生很多开销代码。另请参阅How to create type safe enums?
答案 2 :(得分:0)
所以我理解当你声明一个枚举时,你无法控制它的大小和
typedef struct {
appliance_type_t appliance_type;
voltage_t voltage;
uint16_t crc;
} appliance_t;
你想要的,例如voltage_t voltage
具有特定尺寸,请说uint8_t
,但您无法指定。{/ p>
我不知道您是否可以在枚举上提供大小限制,但您可以使用两种机制。在这两种机制中,您指定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;
所以甚至不给编译器&#34;枚举类型安全&#34; (只检查/使用来自域的值),所以即使用我展示的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'