字节对齐,依据的是当系统要访问特定类型的变量时,必须在特定的内存地址访问,这就要求各种类型的变量按照一定的规则在空间上排列,而不是顺序的逐个排放。其本质就是,字节对齐可以提升存取效率,也就是空间换时间。
为什么要字节对齐?
之前博主有研究过这个字节对齐相关的问题,欢迎大家来一同讨论!
链接直通车:STM32解决读写Flash失败进入HardFault的问题
每个平台对数据存放的对齐要求都不同,如果不按照适合该平台的数据存放对齐要求,会造成存取效率上的损失。比如某个平台每次读取数据时都是从偶地址开始的,如果一个int类型(32位系统)变量存放在偶地址开始的地方,那么一个读周期就能读出这32bit数据,而如果该变量存放在奇地址开始的地方,那么就需要两个读周期,并且要对这两次读出的结果进行高低字节拼凑才能得出这32bit数据,明显在读取效率上就低了很多。
举个例子来说明这个字节对齐的问题
背景知识:
1、基础数据类型的自身对齐值(32位系统)
char类型自身对齐为1字节,short类型自身对齐为2字节,int、float、long类型自身对齐均为4字节,double类型自身对齐为8字节。
2、结构体或类的自身对齐值
其成员中自身对齐值最大的值。
3、指定对齐值
通过预编译指令 #pragma pack (value)来指定对齐值的大小value,取消自定义对齐值使用 #pragma pack()。
4、数据成员、结构体和类的有效对齐值
其自身对齐值和指定对齐值中较小的那个值。
例子1:如下两个结构体A、B
struct A
{
char a;
short b;
int c;
};
struct B
{
short b;
int c;
char a;
};
请问上面两个结构体的大小分别是多少?
答:对于结构体A和结构体B,a是char类型,占用1字节;b是short类型,占用2字节;c是int类型,占用4字节。因此结构体A和结构体B的自身对齐值为4字节。又因为结构体类型数据是按照顺序存储结构逐个向后排列的,因此结构体A的大小为8字节,结构体B的大小为12字节。其对应的存储方式如下图。
例子2:为C、D结构体指定对齐值
#pragma pack(2) //指定2字节对齐
struct C
{
char a;
int b;
short c;
};
#pragma pack() //取消指定对齐,恢复缺省对齐
#pragma pack(1) //指定1字节对齐
struct D
{
char a;
int b;
short c;
};
#pragma pack() //取消指定对齐,恢复缺省对齐
请问上面两个结构体的大小分别是多少?
答:对于结构体C,由于其自身对齐值为4字节,而指定对齐值为2字节,所以该结构体的有效对齐值为较小的2字节,因此结构体C的大小为8字节。对于结构体D,同理可以得知大小为7字节。其对应的存储方式如下图。
总结,整个程序在给每个变量进行内存分配时都遵循字节对齐原则,虽然这会造成内存空间的浪费,但是却能换来程序执行效率的提升。