内存对齐

来源:互联网 发布:ipad看淘宝不能横屏 编辑:程序博客网 时间:2024/04/26 09:18
 一、为什么会有内存对齐?
字,双字,和四字在自然边界上不需要在内存中对齐。(对字,双字,和四字来说,自然边界分别是偶数地址,可以被4整除的地址,和可以被8整除的地址。)
无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问
一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。
某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常。双四字的自然边界是能够被16整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。
简而言之,数据结构(尤其是栈)应该尽可能地在自然边界上对齐,原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

二、对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
规则:
(1)数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
(2)结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
(3)当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

三、示例代码
#include<stdio.h>
struct test
{
char a;
int b;
char c;
double d;
char e;
};
int main(void)
{
printf("%d\n",sizeof(struct test));
return 1;
}
输出结果为24
我的执行环境是Debian Linux(内核2.6.18),gcc 4.1.2,其实与在windows下使用vc的原理是一样的而且不对默认对齐进行修改的话,结果也是一样的。

四、分析
默认情况下,c/c++一般是设置对齐系数为4,对于上面的例子也是如此。从上面的结果我们可以看出结构体test占用的内存大小为24,而不是1+4+1+8+1=15,那么24是如何得到的呢?
按照二(1)中的规则对各个成员占用的内存进行分析如下:
struct test
{
char a; /*长度1 < 4 按1对齐;起始offset=0, 0%1=0,存放区间[0]*/
int b; /*长度4 = 4 按4对齐;起始offset=1, 4%4=0;存放位置区间[4,7] */
char c; /*长度1 < 4 按1对齐;起始offset=8, 8%1=0,存放区间[8]*/
double d;/*长度8 > 4 按4对齐;起始offset=9, 12%4=0,存放区间[12,19]*/*/
char e; /*长度1 < 4 按1对齐;起始offset=20, 20%1=0,存放区间[20]*/
};
在按照二(2)中的规则对结构体整体占用的内存进行分析:
整体对齐系数为min(对齐系数,max(成员占用内存大小))=min(4,8)=4
经过上面的分析test的成员共占用内存区间[0,20],大小为21个字节,然后进行整体对齐,需要满足整体为整体系数4的倍数,那么最近的大小就是24了,所以结构体test占用的内存空间为24字节。

五、其它
1. 在编写代码时候可以通过#pragma pack(n),n=1,2,4,8,16来灵活控制内存对齐的系数,当需要关闭内存对齐时,可以使用#pragma pack()实现。
2. 注意事项
内存对齐可以大大的提高编译器的处理速度,但不是任何时候都是必需的,有的时候不注意的话,还可能出现意想不到的错误!最典型的情况就是网络通信程序的编码中,一定要在定义结构体或者联合之前使用#pragma pack()把内存对齐关闭,这是因为远程主机通常不知道对方使用的何种对齐方式,通过socket接收的字节流,然后按照字节解析得到对应的结果,如果使用内存对齐,远程主机很哟可能会得到错误的结果!这种情况曾经指导上机时遇到过,而且属于比较隐蔽的错误,debug了好久才发现问题出在这里。
3. 优化结构体
虽然内存对齐可以提高运行效率,但是却浪费了内存,在现代PC上通常不会在乎这点小的空间,但是在一些内存很小的嵌入式设备上,可能就要锱铢必较了。其实我们发现在不影响功能的前提下,可以调整成员的顺序来减少“内存空洞”带来的浪费。如果三.中的结构体代码可以调整为
struct test
{
char a;
char c;
char e;
int b;
double d;
}
这个时候整个结构体占用的内存空间将会从上面的24减少到16。

其他资料:http://www.oschina.net/bbs/thread/14304

转自:http://my.oschina.net/xiangxw/blog/11277

原创粉丝点击