内存对齐

来源:互联网 发布:秃鹰配件淘宝上的别名 编辑:程序博客网 时间:2024/05/16 18:03

内存对齐

一、背景

首先我想对“对齐”这个名词做一个自己的理解,我认为对齐不是从静态意义上来说的,而是从动态意义上来说的,是相对于CPU读取过程来说的。所谓对齐就是CPU读取某个变量时,使CPU读取的次数最少的内存排列方式。比如就拿32位(4字节)系统来说,long类型的数据CPU读取一次就能完成读取的,说明此数据是对齐的,如果读取两次才读取完成,说明是未对齐的;double数据类型的数据CPU读取两次就能完成读取的,说明是对齐的,如果读取三次或三次以上的为未对齐。

内存对齐你可能都没有听说过,这很正常,因为他对于使用高级语言(c、c++、java、object-c、c#等)编程的人来说直接接触到的并不多。内存对齐的工作会由你所使用的编译器来做,所以这个工作对于大多数人来说十分的陌生,其实它时时刻刻都在我们身边。你点击开发工具的编译按钮时,此时其实你已经在使用它了。

虽然这个工作编译器会帮我们完成,但我们了解它的实现方式还是有必要的,这样我们就能更好的组织我们的数据结构,从而减少内存使用,提高运行效率。

内存对齐产生的原因总结起来就两种:

1、平台原因(移植原因):不是所有硬件平台都能访问任意地址上的任意数据的;有些硬件平台只能在某些地址处取某些特定类型的数据,否则会抛出硬件异常。

2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做出两次内存访问;而对齐的内存访问仅需要一次便可完成。

下面的例子是从性能上来考虑:

通俗的讲就是CPU读取内存不是连续读取的,而是分成块读取的,块的大小只能是1、2、4、8、16(字节数)。下面举个例子:

0 1 2 3 4 5 6 7:这几个数字代表连续的内存地址,0代表内存开始地址。CPU(假设块大小为4)在读取此内存时是分两次读取的,第一次读取0-3,第二次读取4-7。我们现在有一个int类型的数据放在此处,分两种情况,第一种情况,数据的开始地址为0;第二种情况,数据的开始地址为2。

第一种情况:CPU只需读取一次内存便可得到整数的值。

第二种情况:CPU需要读取两次内存才能得到整数的值,第一次的2-3和第二次的4-5组合成一个整数。

第二种情况会大大降低了CPU的性能。在数据量小的时候,你可能感觉不到,如果数据量十分庞大,你就会感觉出来。这里的这个例子主要是在讲性能问题,对于平台原因并未提及。

二、对齐规则:

每个平台上的编译器都有自己默认的“对齐系数”(也叫对齐模数)。这个系数程序员可以通过编译命令“#pragma pack(n)”改变,其中n的取值必须是以下数字中的一个:1、2、4、8、16

我先做一个自定义变量:

minVarLen = min(pack指定长度,结构体(联合体)当前成员变量数据长度);

minStructLen = min(pack指定长度,结构体(联合体)最大数据长度);

规则:

1、数据成员对齐规则:对于结构体(struct)或者联合体(union)中的成员,第一个成员的offset为0;以后每个成员变量的对齐按照minVarLen进行。

2、结构体(联合体)本身对齐规则:在数据成员完成各自对齐的情况下,结构体(联合体)本身也要对齐,对齐将按照minStructLen进行。

3、综合:结合1、2所述,如果pack指定的长度大于或等于结构体(联合体)最大数据成员长度时,pack为无效。

我并不喜欢上面的三条解释,看完后总是很晕(哈哈),相比较而言,我更喜欢下面的解释:

1、数据成员对齐规则:结构体(联合体)的第一个成员的offset为0,以后每个成员变量的offset值必须是minVarLen的倍数。

2、结构体(联合体)本身对齐规则:在1的基础上,结构体(联合体)本身也要实现对齐,结构体(联合体)的内存地址必须是minStructLen的倍数。

3、整体:内存中结构体(联合体)的整体大小为minStructLen整数倍。

 

三、例子:

上面我们讲了内存对齐的背景及规则,下面我们用一个例子来验证我们的理论是否正确:

#pragma pack(4)Struet A{          Char c1,          Double d,          Char c2,          Short s,          Int i}#pragma pack void main(){          struct A  struA;          int yushu1 = (int)&struA % (min(4, sizeof(double));          int yushu2 = (int)&struA % sizeof(double);          int yushu3 =( (int)&struA.d – (int)&struA.c1)) % (min(4, sizeof(double));          int yushu4=( (int)&struA.c2 – (int)&struA.d)) % (min(4, sizeof(char));int yushu5=( (int)&struA.s – (int)&struA.c2)) % (min(4, sizeof(short));int yushu6=( (int)&struA.i – (int)&struA.s)) % (min(4, sizeof(int));int yushu7 = (int)sizeof(Struet A) % minStructLen;}

 

运算结果为(以下结果为运行多次的结果,并不是一次,只是多次运行的结果都是一样的):

yushu1 = 0;

yushu2 = 4;

yushu3 = 0;

yushu4 = 0;

yushu5 = 0;

yushu6 = 0;

yushu7 = 0;

结果分析:

yushu1、yushu2:说明规则1是正确的。

yushu3-6:说明规则1是正确的。

Yushu7:说明规则3是正确的,结构体的尺寸为minStructLen整数倍。

这个例子只是为了验证我们的理论是正确的,就不再举其它例子了,如果感兴趣可以自己在工程中试验一些小例子,看看试验结果与理论值是否相符合。

原创粉丝点击