关于枚举字节大小的细节
前言
这个问题是我在移植一段代码从linux到window上的vs上发现的,之前并没有注意到枚举大小这个细节,在测试和思考为什么的过程感觉挺有意思,于是记录下来。首先由于需要对协议的进行协议,所以我在单片机上实现一个结构体来对接受的数组进行解析。具体实现如下:
typedef enum
{
READ = 0X00,
WRITE= 0X01
}wrdir_e;
typedef enum
{
MCU2PC = 0X00,
PC2MCU = 0X01
}send_e;
typedef struct __msg
{
uint8_t stx;
uint8_t len;
union
{
uint8_t flag;
struct
{
wrdir_e wrdir: 1;
send_e dir : 1;
uint8_t rsv : 6;
};
};
int16_t crc;
}msg_t;
int main(void)
{
uint8_t test_arr[] = {0x5a, 0x23, 0x11, 0x23, 0x64};
msg_t *pdata = (msg_t*)test_arr;
printf("msg frame header is %x\r\n", pdata->stx);
printf("msg payload length is %x\r\n", pdata->len);
printf("msg wridir is %x\r\n", pdata->wrdir);
printf("msg dir is %x\r\n", pdata->dir);
printf("msg rsv is %x\r\n", pdata->rsv);
printf("msg crc is %x\r\n", pdata->crc);
printf("test code \r\n");
printf("sizeof msg_t is %d\r\n", sizeof(msg_t));
return 0;
}
这句代码是为将串口接受的到数据根据协议进行解析,串口的数据接收到数组中进行缓存,由于数据的格式是连续,如果使用结构体进行解析可以提高代码的可读性和可操作性。但是前提是结构的内部空间需要连续。但是在结构体中使用枚举类型,这就需要知道枚举类型占用几个字节。
通过对代码的运行结果说明编译器定义的枚举类型是4个字节。
枚举字节
我在网上查到这个说明,这句话说明枚举类型完全取决为编译器。
标准C并没有明确规定枚举类型占用空间的大小,标准中只是说“枚举类型的尺寸是以能够容纳最大枚举子的值的整数的尺寸”,同时标准中也说明了:“枚举类型中的枚举子的值必须要能够用一个int类型表述”,也就是说,枚举类型的尺寸不能够超过int类型的尺寸,但是是不是必须和int类型具有相同的尺寸呢?上面的标准已经说得很清楚了,只要能够容纳最大的枚举子的值的整数就可以了,那么就是说可以是char、short和int。”不同的C编译器,对enum类型的大小作了不同的定义,而程序员是不能认为的修改枚举类型占用空间的。
但是我们真的没有办法进行枚举类型的定义,比如我只有两个枚举量如我的代码wrdir_e这个枚举类型只需要两个,但是编译器开辟4个字节这明显不太合适,并且我们协议定义的大小就一个字节,这样就无法直接进行强制转换结构实现解包的功能。
所以这里我就研究是否存在方法可以设置枚举类型的大小。
通过研究我发现如果要设置枚举类型为1一个字节,那么我需要使用gcc -fshort-enums 这个命令对.c文件进行编译.编译后我们实现了枚举的字节一个字节
扩展思考
对于不同的编译器的,我们实现枚举类型的字节大小是不确定,通过测试我们在ARM编译器到windows系统的是字节是不一致的.所以enum长度不确定会带来可移植性问题,如果第三方库API接口使用enum类型,编译和调用库时一旦有关enum长度的编译器设置不一致,API接口层对数值的解析就不匹配。比如上层应用编译时没有用-fshort-enums,默认用4字节空间来存储使用enum变量,而编译库时设置了fshort-enums,则库内部此enum size可能为1。当把enum变量地址传进API时,内部只修改变量最低字节,高3字节值无变化(内容随机),API返回时,上层使用的4字节enum变量值就可能随机,(潜规则篇之API接口)
所以这样可以得到一个结论:
内部代码使用enum类型优于define,但对外API接口尽量避免用enum型。