关于内存对齐的小总结

来源:互联网 发布:贴贴相传网络 编辑:程序博客网 时间:2024/05/17 00:07

对齐规则:

 

1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照 #pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

 

2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

 

3、结合1、2推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

 

4.各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。

 

5.各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节自动填充。

 

6.同时为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。

ps:不同编译器默认的最大对齐字节数是不一样的,比如vc==8,gcc==4,可以通过#progma pack (n)来修改,分析程序的时候要注意编译器的区别

以下示例转自:
http://www.cppblog.com/cc/archive/2006/08/01/10765.html
我们还是先来看一段简单的程序:

#include <iostream>using namespace std; struct X1{   int i;//4个字节   char c1;//1个字节   char c2;//1个字节};struct X2{char c1;//1个字节int i;//4个字节char c2;//1个字节};struct X3{char c1;//1个字节char c2;//1个字节int i;//4个字节};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,这三个结构的主要区别就是内存数据摆放的顺序,其他都是一样的,另外程序输入了几种基本类型所占用的字节数,以及我们这里的三个结构所占用的字节数。

这段程序的运行结果为:

long 4
float 4
int 4
char 1
x1 的大小 8
x2 的大小 12
x3 的大小 8


结果的前面四行没有什么问题,但是我们在最后三行就可以看到三个结构占用的空间大小不一样,造成这个原因就是内部数据的摆放顺序,怎么会这样呢?

下面就是我们需要讲的内存对齐了。

内存是一个连续的块,我们可以用下面的图来表示,  它是以4个字节对一个对齐单位的:

                                                   图一

让我们看看三个结构在内存中的布局:

首先是 X1,如下图所示


    X1 中第一个是 Int类型,它占有4字节,所以前面4格就是满了,然后第二个是char类型,这中类型只占一个字节,所以它占有了第二个4字节组块中的第一格,第三个也是char类型,所以它也占用一个字节,它就排在了第二个组块的第二格,因为它们加在一起大小也不超过一个块,所以他们三个变量在内存中的结构就是这样的,因为有内存分块对齐,所以最后出来的结果是8,而不是6,因为后面两个格子其实也算是被用了。

    再次看看X2,如图所示


    X2中第一个类型是Char类型,它占用一个字节,所以它首先排在第一组块的第一个格子里面,第二个是Int类型,它占用4个字节,第一组块已经用掉一格,还剩3格,肯定是无法放下第二Int类型的,因为要考虑到对齐,所以不得不把它放到第二个组块,第三个类型是Char类型,跟第一个类似。所因为有内存分块对齐,我们的内存就不是8个格子了,而是12个了。


再看看X3,如下图所示:



关于X3的说明其实跟X1是类似的,只不过它把两个1个字节的放到了前面,相信看了前面两种情况的说明这里也是很容易理解的。

|------------------------------------------------------------------------------------------分割线------------------------------------------------------------------------------------------|

关于union的内存对齐问题就比较简单了
例如

union x5{double a; // 8int b;char c;char d[9]; // 大小为9};



union占用内存的长度等于最大的成员的长度,且按照对齐规则2,选择以double的长度进行整体对齐,因此占用两个double的长度即16
如果剔除其中的 double型数据,则结果应该为12,即 3个int的长度


关于对齐规则的第四条验证一下例子可得
struct x{char b; //1开始位置放在内存虚拟地址为0的地方(规则①)int a;  //4放在内存虚拟地址为4的地方(规则④)char d;//1放在内存虚拟地址为8(4+4)的地方short c;//2放在内存虚拟地址为10的地方  (规则④)char e;//1放在内存虚拟地址为12的地方};//整体以int大小对齐(注意 0~11已经是3X4个int了,所以12这个位置放了e,整体对齐之后的大小sizeof(x) = 16)