希望本是无所谓有,无所谓无的,这正如脚下的路,其实地上本没有路,走的人多了,也便成了路....原创不易,文章会持续更新,感谢您的关注
1.问题由来
MCU给上位机发送的一帧数据中,总是多一个字节,调试很久后发现gcc编译器会把结构体所占空间按四字节对齐。
2.复现
(1)声明结构体和枚举类型
typedef enum{
EN_INDIVIDUAL = 0x01,/*个人*/
EN_MANUFACTURER ,/*制造商*/
EN_ENGINEER ,/*工程师*/
}enUserLevel_t;
typedef enum{
EN_BOOTLOADER = 0x01,/*bootloader*/
EN_CONTROLLER_APP ,/*控制器app*/
EN_PHONE_APP ,/*手机app*/
EN_PC ,/*电脑上位机*/
}enDeviceType_t;
typedef struct {
enDeviceType_t ubDeviceType;
enUserLevel_t ubUserLevel;
uint8_t ubMainCmd;
uint16_t usSubCmd;
uint8_t ubDescribeInfo;
uint32_t ulParameterLength;
uint8_t* pParameterBuffer;
uint32_t ulCrcCheckCode;
}tagFrame_t, *pFrame_t;
(2)程序
__attribute__((optimize( 0 ) ))int main(void)
{
/*因为p定义了没使用,加上__attribute__((optimize( 0 ) ))*/
/*可以让gcc不要优化这个函数*/
tagFrame_t frame;
pFrame_t p = &frame;
uint8_t arrTxBuffer[1024] = {0x00};
unit32_t length = sizeof(tagFrame_t);
frame.ubDeviceType = 1;
frame.ubUserLevel = 2;
frame.ubMainCmd = 3;
frame.usSubCmd = 0x1234;
frame.ubDescribeInfo = 5;
frame.ulParameterLength = 0xabcd;
frame.pParameterBuffer= (uint8_t*)0x1234;
for (int8_t i = 0; i < length; i++)
{
arrTxBuffer[i] = ((uint8_t*)frame)[i];
}
sendFrameData(arrTxBuffer, length);
}
(3)看内存
从内存可以看出,因为tagFrame_t 结构体的前三个字节加起来一共3个字节,不足4字节,所以gcc编译器会自动补一个字节,筹齐4字节为一组。
Frame_t的第四个字节是从0x20008FD8开始的,而不是从0x20008FD7开始的,0x20008FD7地址处的0就是gcc编译器补的。也就能解释为什么通过sendFrameData函数把arrTxBuffer数组中的内容一个一个字节发送到上位机时,数据老是对不上。
3.解决方法
修改结构体的声明,人为配成4字节对齐:
typedef struct {
enDeviceType_t ubDeviceType;
enUserLevel_t ubUserLevel;
uint8_t ubDescribeInfo;
uint8_t ubMainCmd;
uint32_t usSubCmd;
uint32_t ulParameterLength;
uint8_t* pParameterBuffer;
uint32_t ulCrcCheckCode;
}tagFrame_t, *pFrame_t;
4.结论
编译器为gcc时,有以下结论:
(1)C语言的枚举占1个字节。
(2)gcc编译程序时,会自动把结构体按4字节方式对齐。
(3)默认情况下,arm处理器是小端模式。0x1234这个数据,0x12是高8位,在高地址0x20008FD9中;0x34是低8位,在低地址0x20008FD8中。
专注于 嵌入式 和 qt知识分享
欢迎扫码关注
“嵌入式工程师成长之路”
作者原创视频