测试环境:STM32H7R3+MDK 5.39+AC5
注:PC、PowerPC等环境不适用本文。
一.字节对齐
一般采用自然对齐(默认方式),提高数据存取速度。
采用1字节对齐,变量在内存中无空隙,紧密存储,节省存储空间。另外可配合union,在通信时进行数值(void*)与tx/rx缓冲区(uint8_t*)的相互转换。
1.默认方式
#include <stdint.h>
typedef struct {
uint8_t A;
uint16_t B;
uint16_t C;
}Test_T;
int main(void)
{
Test_T Test={
.A=0x11,
.B=0x2222,
.C=0x3333,
};
return 0;
}
内存图:
为保证自然对齐,成员在内存中有空隙。
uint16_t成员A占2字节,起始地址0x200003ED%2=1,非2字节对齐,而0x200003EE%2=0,满足对齐条件。
2.强制1字节对齐
#include <stdint.h>
#pragma pack(push, 1)
typedef struct {
uint8_t A;
uint16_t B;
uint16_t C;
}Test_T;
#pragma pack(pop)
int main(void)
{
Test_T Test={
.A=0x11,
.B=0x2222,
.C=0x3333,
};
return 0;
}
内存图:
变量在内存中紧密排列,无空隙
二.位域
C语言最小的数据类型是字节(8bit) ,我们只能选择8/16/32bit去存储数据。在某些应用中,数据需要更灵活的数据类型来存储,如开关量0/1只需要1bit来存储;考试成绩0~100只需要7bit来存储。
通过位域,可在结构体实现指定bit数的存储。且成员数据类型只能为整型。
typedef struct {
uint8_t A:1;
uint16_t B:4;
uint16_t C:7;
}Test_T;
Test_T Test={
.A=0b1,
.B=0b0000,
.C=0b1111111,
};
此时Test_T的各成员数据类型(uint8_t、uint16_t)的作用,是限制位域成员的最大bit数,以及该位域的值应被如何解释。
在未指定1字节对齐时(#pragma pack(push, 1)),位域结构体在内存中仍有空隙,对齐方式与成员类型以及位域大小有关。详见博文(C语言笔记)位域的详解使用(含内存对齐)。所以在需要形成连续比特流的场合,需要位序配合1字节对齐使用。
注:Test_T的各成员的大小是非字节倍数的,无法用sizeof(Test_T.A)计算空间。但由各位域成员组成的结构体变量,仍可用sizeof(Test_T)计算大小。Test_T的各成员也无法通过指针操作。
三.联合体
在通信中使用,可将有意义的数值转为字节缓冲区,即可实现快速组帧。
四.三者配合使用
如下有效利用资源,实现通信协议对数据的高度压缩。