希望本是无所谓有,无所谓无的,这正如脚下的路,其实地上本没有路,走的人多了,也便成了路....原创不易,文章会持续更新,感谢您的关注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知识分享
欢迎扫码关注
“嵌入式工程师成长之路”

作者原创视频
