参考: 内存对齐原则
#pragma pack(n)
1内存对齐原则
先看一个例子:
#include<iostream>
using namespace std;
struct S1
{
char c1;
char c2;
int i1;
};
struct S2
{
char c1;
int i1;
char c2;
};
int main()
{
cout <<"sizeof(S1)"<< sizeof(S1)<<endl;
cout << "sizeof(S2)"<<sizeof(S2)<<endl;
return 0;
}
运行结果如下:
之所以会出现上面的结果,是因为结构体存在内存对齐原则,具体原则如下:
- 每个结构体变量的地址空间为[0,sizeof(S)],而结构体中的第一个成员变量偏移量为0,即从头开始。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍地址处,对齐数 = min{编译器默认对齐数,该成员变量大小},在windows中,编译器默认对齐数为8;
- 结构体大小为最大对齐数的整数倍;
- 如果嵌套结构体,则嵌套结构体当成一个普通成员变量处理。
因此,上面程序中结构体S1的内存分布图如下:
S2的内存分布图为:
2 更多例子
嵌套结构体
#include<iostream>
using namespace std;
struct S1
{
double d1;
double d2;
};
struct S2
{
char c1;
S1 s1;
char c2;
};
int main()
{
cout <<"sizeof(S1):"<< sizeof(S1)<<endl;
cout << "sizeof(S2):"<<sizeof(S2)<<endl;
return 0;
}
类的对齐
对于类的某个实例(对象)而言,成员函数、静态变量都不会占空间,只有成员变量占空间,而只考虑成员变量的类相当于一个结构体,其对齐原则同上:
#include<iostream>
using namespace std;
class S1
{
char c1;
char c2;
int i1;
};
class S2
{
static double static_c;//静态成员
char c1;
char c2;
int i1;
int fun1()//成员函数
{
c1 = 'c';
return 0;
}
};
int main()
{
S1 s1;
S2 s2;
cout <<"sizeof(s1):"<< sizeof(s1)<<endl;
cout << "sizeof(s2):"<<sizeof(s2)<<endl;
return 0;
}
为什么要对齐
CPU把内存当成一块一块的,块的大小可以是2字节、4字节、8字节等,因此CPU读取内存时是一块一块读取的,块的大小成为内存读取粒度,可以用预编译指令#pragma pack(n)
来设置。
如果内存不对其将会影响CPU的读取效率,例如:
假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4),分两种情况讨论:
1)数据从0字节开始
2)数据从1字节开始
解析:
1)当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,读取一次就可以了。
2)当数据从1字节开始的时候,问题很复杂,首先先将前4个字节读到寄存器,并再次读取4-7字节的数据进寄存器,接着把0字节,5,6,7字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器,也就是要读取两次才能读到该数据。