内存对齐的作用
1.提高cpu的内存访问速度
cpu按块读取内存,一次读取一个块,块的大小可以是2、4、6、8个字节。
假如块的大小是4个字节,那么当一块数据的偏移量为4,起始地址为0,那么cpu刚好可以把0、1、2、3字节的所有数据读取到寄存器,但是假如数据的起始地址为1,那么cpu就需要读取两个块的数据,也就是首先读取0~3字节,再读取1~7字节的数据之后,再把5、6、7字节的数据移除,再合并1、2、3、4字节的数据才能进入寄存器。
因此,当cpu没有进行内存对齐时,cpu会进行额外的处理才能把数据读取到寄存器,这降低了cpu的内存访问速度
2.有利于平台移植
不是所有硬件平台都能访问到任意地址上的任意数据,在嵌入式系统中,某些硬件只能在某些特定地址处取特定类型的数据,否则就会抛出异常,从而危及系统的安全性和稳定性。在这种情况下,内存对齐显然利于平台移植
内存对齐的基本原则
- 结构体的第一个成员的偏移量为0,且每个成员的内存占用大小都是有效对齐值的整数倍,不足则补。
- 结构体的总大小为有效对齐值的整数倍,不足则补。
有效对齐值的确定
- 当未明确指出有效对齐值时,以结构体中占用内存最大的数据成员的内存大小为有效对齐值
- 当使用#prama(n)指出有效对齐值(n即为指出的有效对齐值)时,以n和结构体最大的数据成员大小的最小值作为有效对齐值。
- 当用__attribute__ ((packed))指定长度时,强制按照此值为结构体的有效对齐值
空类的内存大小
一个空类的占用内存大小为1个字节,并不是为0,有关空类的相关说明以及内存占用问题详见
内置类型成员的对齐
case1
class A {
char c1;
char c2;
int a;
};
类A的占用字节大小:8.
1.确定类A的有效对齐值:
也就是内存块的大小为4
n=max{sizeof(char),sizeof(int)}=4;
2.分析每个数据成员的内存占用情况:
c1:char占用1个字节,直接存放
c2:占用1个字节,1+1=2<4可以直接存放到上个内存块中
a:占用4个字节,2+4=6>4,也就是说整型a无法放入上一个内存块中,此时需要等待下一个内存块的到来,并直接存放
故类A的内存内存占用大小为1+1+(2)+4=8;
说明,括号内为补足字节
case2
class B {
char c1;
int a;
char c2;
};
类B的占用字节大小:12
同case1分析,类B的有效对齐值也是4,则c1占用一个字节后,a无法存放一个块,故c1进行补足,之后a占用一个块,c2也占用一个字节后补足,故1+(3)+4+1+(3)=12
case3
class C {
double d;
int a;
char c;
};
有效值为8(内存块大小),d占用一个块,a占用4个字节,c可以继续存放到a所占用的块
故为8+4+1+(3)=16
case4
class D {
int a;
double d;
char c;
};
同上分析,有效对齐值为8,故总大小为4+(4)+8+1+(7)=24
结构体数据成员的对齐
问题:假如一个类中有一个结构体数据成员,那么这个类的内存占用大小该如何分析
class BigData
{
char array[31];
};
class Data
{
BigData bd;
int integer;
};
void sol() {
cout << __alignof(Data) << endl;//查看Data的有效对齐值
cout << sizeof(BigData) << " " << sizeof(Data) << endl;
}
首先确定Data的有效对齐值
在以上案例中,Data类中有一个数据成员是BigData类,而BigData类的有效对齐值为4,这是由于在BigData类中,数据成员Data类对象bd的有效对齐值为1,而BigData的另一个数据成员integer的大小为4,故BigData的有效对齐值为4。
事实上,当我们使用__alignof关键字查看int的有效对齐值时,结果为4,也就是说对于内置类型的数据,它们的有效对齐值就是其占用大小本身。
确定了Data的有效对齐值后,再看每个数据成员的内存占用情况
bd成员共31个字节,不足4的整数倍,故补足一个字节,integer刚好占用一个块,故总大小为31+(1)+4=36
由上述实验我们可以得出结论,对于一个类的有效对齐值,取所有数据成员的有效对齐值的最大值作为该类的有效对齐值
继承的对齐
class A
{
int i;
char c1;
};
class B :public A
{
char c2;
};
class C :public B
{
char c3;
};
sizeof(C)结果是多少呢,gcc和vs给出了不同的结果,分别是8、16。
- gcc:C相当于把所有成员i、c1、c2、c3当作是在一个class内部,(先继承后对齐)
- vs:对于A,对齐后其大小是8;对于B,c2加上对齐后的A的大小是9,对齐后就是12;对于C,c3加上对齐后的B大小是13,再对齐就是16 (先对齐后继承)