最近写蓝牙协议,数据必须按协议中指定的报文进行传输,但由于默认处理器有字节对齐,而一旦多一个字节都会导致通信错误,所以这里记录一下字节对齐的处理。
1 介绍
CPU不会一个字节一个字节地读或写内存,而是一次访问2/4/6/8/16/32/64字节,因为这比读一个字节要快得多。假设CPU按四字节对齐,我们把四字节称为一个chunk,此时要读内存0x03-0x06的内容,CPU就会读取0x00~0x03和0x04-0x07两个chunk,然后把两个chunk读到的内容移位并组合返回给用户。
假设CPU按照4字节进行对齐,现在来看一个结构体:
struct S1{
char a;
char b;
};
CPU就会在结构体最后填充两个字节,以保证字节对齐。注意这个对齐不总是在最后的,而是数据类型所对应的变量需要字节对齐,比如
struct S2{
uint8_t a;
uint16_t b;
};
由于uint16_t
为2字节,需要2字节对齐,而前面的uint8_t
只有1字节,故会在a
后面填充1个字节,而对于结构体变量S2
来说,其需要四字节对齐,故还会在b
后面填充一个字节。
- 变量类型占多少个字节就需要几字节对齐,比如uint8_t为1字节,则它放在内存中任何位置都可以
2 设置字节对齐
2.1 pack注释
我们可以使用pack
pragma来设置对于struct、union和class的字节对齐:
1、保存当前系统字节对齐的状态,对指定的结构对齐完恢复原先对齐数
#pragma pack(push, N)
待字节对齐的struct、union和class
#pragma pack(pop)
注:
#pragma pach(push,N)等价于
#pragma pack(push)
#pragma pack(N)
2、作用同上
#pragma pack(N)
待字节对齐的struct、union和class
#pragma pack()
看一个例子:
#pragma pack(push, 1)
struct S3 {
char m1; // 1-byte
double m2; // 8-byte
};
#pragma pack(pop)
这样就让这个结构体进行1字节对齐,即不进行对齐。此时结构体的大小就是9字节。
2.2 __attribute__关键字
__attribute__ ((packed))
:取消在编译过程中的优化对齐,按照实际占用字节数进行对齐__attribute((aligned (n)))
:让所作用的结构体成员对齐在n字节边界上。如果结构体中有成员变量的字节长度大于n,则按照最大成员变量的字节长度来对齐。
struct person1{
char *name;
int age;
char score;
int id;
}__attribute__ ((packed));
struct person2{
char *name;
int age;
char score;
int id;
}__attribute((aligned (n)));