定义位域(bitfield)时要注意数据类型

定义位域(bitfield)时要注意数据类型


在EEPROM中保存历史告警时,设计为告警码4个字节,开始时间4个字节,结束时间4个字节,共计12个字节。


// 告警时间
typedef struct stAlarmTime
{
UINT16 Year : 6; // 从2000年开始算,可表示2000~2063年
UINT16 Month : 4; // 1-12月
UINT16 Day : 5; // 1-31
UINT16 Hour : 5; // 0-23
UINT16 Min : 6; // 0-59
UINT16 Sec : 6; // 0-59
}ST_ALARM_TIME;


// 历史告警
typedef struct stHistAlarm_t
{
UINT32 uiAlarmCode;
ST_ALARM_TIME stStartTime;/*告警出现的时间*/
ST_ALARM_TIME stEndTime;/*告警结束的时间*/
}ST_HIST_ALARM;


以为sizeof(ST_HIST_ALARM)是12个字节,其实不然,它是16个字节。


编译环境:
MDK-ARM V4.22a




经过测试,
去掉时间,只留下uiAlarmCode,则sizeof(ST_HIST_ALARM)是4个字节, (1)
删除stEndTime,只保留stStartTime,则sizeof(ST_HIST_ALARM)是12个字节!(2)
再恢复stEndTime,则sizeof(ST_HIST_ALARM)是16个字节! (3)


原因分析:
(1)因为是小端模式,结构体(位域)定义时,先定义的占用低字节(低位)。
因此Year占用bit0~bit5,Month占用bit6~bit9,Day占用bit10~bit14。
然而由于数据类型定义的是UINT16(双字节),刚才这个“双字节”只剩下1位了,不足以保存Hour,
需要重新开辟一个“双字节”。所以,Hour和Min占用第二个“双字节”。同理Sec也需要一个单独的“双字节”。
综上,sizeof(ST_ALARM_TIME)占了6个字节。
(2)当ST_HIST_ALARM结构体只包含uiAlarmCode和stStartTime时,前者占4个字节,后者占6个字节。
但由于整个结构体也需要对齐,对齐方式为结构体的大小需要被每一个元素的大小整除。因此不能是10,而是12.
(3)uiAlarmCode占4个字节,stStartTime和stEndTime各占6个字节,总共16个字节。刚好对齐。




经过测试,找到原因:sizeof(ST_ALARM_TIME)占了6个字节。

解决办法:

把ST_ALARM_TIME中的UINT16改为UINT32,则sizeof(ST_ALARM_TIME)就是4个字节。


你可能感兴趣的:(定义位域(bitfield)时要注意数据类型)