一、为什么会有内存对齐?
字,双字,和四字在自然边界上不需要在内存中对齐。(对字,双字,和四字来说,自然边界分别是偶数地址,可以被4整除的地址,和可以被8整除的地址。)
无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。
某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常。双四字的自然边界是能够被16整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。
简而言之,数据结构(尤其是栈)应该尽可能地在自然边界上对齐,原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
二、C++内存对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
对齐规则:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照 #pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结合1、2推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
4.各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
5.各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节自动填充。
6.同时为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
(此两点最为重要)
#include <iostream>
using namespace std;
class X1
{
int i;//4个字节
char c1;//1个字节
char c2;//1个字节
public:
X1()
{
i=0x98765432;
c1=0x12;
c2=0x21;
}
};
class X2
{
char c1;//1个字节
int i;//4个字节
char c2;//1个字节
public:
X2()
{
i=0x98765432;
c1=0x12;
c2=0x21;
}
};
class X3
{
char c1;//1个字节
char c2;//1个字节
int i;//4个字节
public:
X3()
{
i=0x98765432;
c1=0x12;
c2=0x21;
}
};
int main()
{
cout<<"long "<<sizeof(long)<<"\n";
cout<<"float "<<sizeof(float)<<"\n";
cout<<"int "<<sizeof(int)<<"\n";
cout<<"char "<<sizeof(char)<<"\n";
X1 x1;
X2 x2;
X3 x3;
cout<<"x1 的大小 "<<sizeof(x1)<<"\n";
cout<<"x2 的大小 "<<sizeof(x2)<<"\n";
cout<<"x3 的大小 "<<sizeof(x3)<<"\n";
return 0;
}
图中类对象x1,x2,x3的地址分别为0x12ff5C,0x12ff48,0x12ff38:
![](https://img-my.csdn.net/uploads/201204/07/1333778917_2792.jpg)
![](https://img-my.csdn.net/uploads/201204/07/1333778938_3421.jpg)
![](https://img-my.csdn.net/uploads/201204/07/1333778961_4424.jpg)
![](https://img-my.csdn.net/uploads/201204/07/1333778973_7648.jpg)