为什么要内存对齐?
尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存。
现在考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据。假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的联系四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器。为了访问未对齐的内存,处理器可能需要作两次内存访问;而对齐的内存访问可能仅需要一次访问。总的来说,结构体的内存对齐是拿空间来换取时间的做法。
内存对齐规则
- 每个变量相对于起始位置的偏移量必须是该变量类型大小的整数倍,不是整数倍空出内存,直到偏移量是整数倍为止;
- 如果设置了内存对齐(对齐参数)为 i 字节,结构体中最长数据类型长度为j,那么整体有效对齐值n = min(i, j) ;
- 每个成员对齐规则:类中第一个数据成员放在offset为0的位置;对于其他的数据成员(假设该数据成员字节数为k),放置的起始位置offset应该是 min(k, n) 的整数倍;
- 最后整个结构体或类的大小应该是n的整数倍。
对齐参数
windows中,VS编译器下,默认对齐数为8; Linux中,默认对齐数为4 ;
设置对齐参数可在结构体struct之前加上预编译命令#pragma pack(对齐数),在struct之后加上#pragma pack;便可以设置对齐参数。
对齐参数不能任意设置,只能是内置类型已有的字节数,如:char(1)、short(2),int(4),double(8)…,不能是3,5…任意数
#pragma pack(4)
struct A
{
int a;
char b;
double c;
char d;
};
#pragma pack();
实例分析
#include<iostream>
using namespace std;
#pragma pack(2) // 设置对齐参数2,整体n = min(2, sizeof(int))) = 2
struct A
{
char c; //放在位置0,位置区间[0]
int i; //4 > n, 那么放置起始位置应该是2的倍数,即2,位置区间为[2~5]
short s; //2 = n,那么放置起始位置应该是2的倍数,即6,位置区间为[6~7]
};
#pragma pack() // //取消设置的默认对齐数,还原为默认
int main()
{
struct A a;
cout << sizeof(a); // 8
return 0;
}
参考:https://zhuanlan.zhihu.com/p/30007037