c++内存对齐
1.为什么要内存对齐
内存存取粒度:大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度。
以4字节作为存取粒度的处理器,只能从地址为4的倍数的内存开始读取数据。
假如一个int变量存放在从地址1开始的四个字节地址中,该处理器去取数据时,第一次要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址);第二次从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器,这会非常影响性能。
归根结底是编译器想通过空间换时间,通过适当增加padding,使每个成员的访问都在一个指令里完成,而不需要两次访问再拼接,提高内存的访问效率。
2.内存对齐规则
对齐系数:#pragma pack(n)。
对齐单位:min(#pragma pack(n),结构体中最长数据类型长度)。
内存对齐规则:
[1]内部对齐:第一个数据成员放在offset为0的地方,以后每个成员相对于结构体首地址的offset都是 min(该成员大小,对齐单位)的整数倍,如有需要编译器会在成员之间加上填充字节。
[2]外部对齐:结构体的总大小为对齐单位的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
因此看一个结构体大小时,首先根据内部对齐规则累加每个变量的大小,之后再根据外部对齐算出总大小。
例如对于:
#pragma pack(2)
struct aa{
char a;
char b;
int c;
char d;
long long e;
char f;
};
首先:对齐单位 = min(#pragma pack(2),sizeof(long long)),因此对齐单位是2。
a放到offset == 0的位置,因为0是【min(a成员大小,对齐单位) == 1】的整数倍。
b放到offset == 1的位置,因为1是【min(b成员大小,对齐单位) == 1】的整数倍。
c放到offset == 2的位置,因为2是【min(c成员大小,对齐单位) == 2】的整数倍。
d放到offset == 6的位置,因为6是【min(d成员大小,对齐单位) == 1】的整数倍。
e放到offset == 8的位置,因为8是【min(e成员大小,对齐单位) == 2】的整数倍。
f放到offset == 16的位置,因为16是【min(f成员大小,对齐单位) == 1】的整数倍。
此时共占用了17个字节,而整个结构体需要是对齐单位(2)的整数倍,因此整个结构体大小是18。