内存对齐的概念
引入代码
众所周知,C++的空类占用1个字节的内存空间,非空类占用的空间与类内的成员有关。
但类中成员所占内存并不是连续的,拿以下代码举例:
#include<iostream>
using namespace std;
class test1
{
char c1;
int a;
char c2;
}t1;
class test2
{
char c1;
char c2;
int a;
}t2;
class test3
{
int a;
char c1;
char c2;
}t3;
int main()
{
cout<<"size of t1="<<sizeof(t1)<<endl;
cout<<"size of t2="<<sizeof(t2)<<endl;
cout<<"size of t3="<<sizeof(t3)<<endl;
system("pause");
}
在32位系统中,int类型占用4字节,char类型占用1字节,并且3个test类成员除顺序外完全一样,理论上输出结果均为6
。
但实际t1,t2,t3的输出为12 8 8
,不仅大小与理论不符,t1所占空间还要大于后两者。
这是因为成员变量的存储并不是连续的,而是根据一定的块大小存储(一般默认为4),这就是所谓的内存对齐。
内存对齐的规则
对齐系数与有效对齐值
首先明确两个概念
-
对齐系数:每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。linux中默认为4,vs中默认为8,可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16来改变这一系数。
-
有效对齐值:是给定值#pragma pack(n)和类中最长数据类型长度中较小的那个,也叫对齐单位。比如vs中默认对齐系数为8,但该类中最长数据类型int为4,则有效对齐值为4。
为方便理解引入以下代码:
#include<iostream>
using namespace std;
class test1
{
char c1;
char c3;
char c2;
}t1;
int main()
{
cout<<"size of t1="<<sizeof(t1)<<endl;
system("pause");
}
虽然默认对齐系数为4,但该类中最长数据类型char为1,所以有效对齐值为1,结果输出为3
内存对齐的具体规则为
- 第一个成员变量放在offset为0的地方,以后每个成员变量的对齐按照有效对齐值进行。
- 在成员变量完成各自对齐之后,类(结构或联合)本身也要进行对齐,对齐将按照有效对齐值进行。
- 类的总大小为最大对其数(每一个成员变量都有一个对其数)的整数倍。
- 如果存在类嵌套,则将嵌套类展开后对每一个成员变量对齐。
值得注意的是,n的缺省数值是按照编译器自身设置,gcc默认为4,合法的数值分别是1、2、4、8、16。
(即编译器只会按照1、2、4、8、16的方式分割内存,其他值无效)
字节边界(重要!)
除了要考虑有效对齐值和对齐系数外,还需要引入字节边界的概念,具体规则为:
char在 1 字节的边界上对齐,short在 2 字节的边界上对齐,int、long和float 在 4 字节的边界上对齐,double在 8 字节的边界上对齐,依次类推
这里引入一个例子:
class test4
{
char c1;
short c2;
char c3;
int c4;
}t4;
如果不引入字节边界的话,则t4理应占用8个字节
(c1c2c3共占4字节,c4独占4字节),但实际上t4在内存中占用空间为12个字节
,具体内存分配可看后续图示
图示
test的内存分配如下
如果把使用#pragma pack(n)
把默认的对齐系数改为1,代码如下
#include<iostream>
using namespace std;
#pragma pack(1)//设定为 1 字节对齐
class test1
{
char c1;
int a;
char c2;
}t1;
class test2
{
char c1;
char c2;
int a;
}t2;
class test3
{
int a;
char c1;
char c2;
}t3;
class test4
{
char c1;
short c2;
char c3;
int c4;
}t4;
int main()
{
cout << "size of t1=" << sizeof(t1) << endl;
cout << "size of t2=" << sizeof(t2) << endl;
cout << "size of t3=" << sizeof(t3) << endl;
cout << "size of t4=" << sizeof(t4) << endl;
system("pause");
}
则此时t1,t2,t3输出结果均为6,t4输出结果为8