内存对齐方式

看到这篇博文题目,你可能会想,什么题目到底啥意思!
首先,你在申请一块空间时,系统会根据你所申请的空间大小,按照一定规则,给他分配一段空间的起始地址,然后你就可以安心的开始写你的数据了。今天,这篇博文便是谈一谈,系统是按照怎么样的规则进行分配空间的。


在正式开始之前,我们先小试牛刀:
struct t1
{
char a;
short b;
int c;
};
请问:sizeof(struct t1)的值是多少?
struct t2
{
char a;
int b;
short c;
};
sizeof(struct t2)又是多少?
答案在这先揭晓,我们先看看后面的内容。




1.首先,我们要说一说为什么要有内存对齐这一套机制。
应该普及我们的是,C语言是一个非常注重效率的语言。在今天,我们内存越来越大时,我们可以用以空间为代价,而换取程序高效率的运行。我想这便是内存对齐出现的原因吧。我们还是先从例子下手,假设我们有个地址是0x00000001,我们要从接下来的四个字节里取一个int型数据。那按照最快的速度只能这样:先取一个字节的char(0x00000001),然后再取两个字节的short(0x00000002 - 0x00000003)然后再取一个char(0x00000004),如此,才能读到一个int值。那么假设,是从0x00000004
开始读值的呢!直接一个四字节的int不久够了。这效率比就是三比一了。


2.好了,看了上面的内容你可能就开始想:系统是怎么对齐的。
对于基本数据类型,其内存的首地址为他长度的整数被即可,对于非基本类型,则按照一下方式对齐:
数组:第一个元素对齐,后面元素自然就对齐了;
联合体:以其元素最长的值为对齐值。
结构体:每个数据类型都需要对齐,具体方法如下:


讲这问题之前又要补充几个小名词:
a.对齐值:即数据的其实地址为D,D%N == 0,N就是他的对齐值。
b.数据类型的自身对齐值:char的自身对齐值就为1,short为2,int float double都为4。
c.结构体和联合体的自身对齐值:他的对齐值为数据结构中,数据类型的自身对齐值最大的值。
d.指定对齐值:指定对齐值在vc下为#pragra pack(n),其中,n便是所谓的指定对齐值了(gcc,vc 默认对齐值为4)。
e.数据成员或者结构体的有效对齐值:即为指定对齐值和自身对齐值中小的那一个。
f.有效对齐值圆整:就是结构体占用的总长度必须为其有效对齐值的整数倍。下面给个例子,你可能会更清楚点:
struct A
{
int a;
short b;
};
假设a分配在0x00000000上,因为int自身对齐值为4,默认的指定对齐也为4,所以a从这里开始分配4个字节,那么,int下一个元素理论的起始地址为0x00000004,short能不能从这开始分配,我们还需要看看其有效对齐值,经过刚才如上分析得,他的有效对齐值为2,0x00000004明显是2的整数倍,所以,short就占用0x00000004-0x00000005.难道,sizeof(struct A)为6?不是,我们还需要根据他的有效对齐值圆整,即(4+2 +2)%4 == 0。这就是有效对齐值圆整了。


说到这,博客前面两个例题你就有了答案把!
我们分析一下t2好了:首先,假设系统给char分配的空间起始地址为0x00000000,因为char的有效对齐值为1,因此下一元素本是从0x00000001,但是因为下一元素是int,他的有效对齐值是4,系统便要在char后面补三个空字节了,int从0x000000004开始分配,int下一元素的起始地址为0x00000008,他是short有效对齐值的整数倍,所以short从他开始分配,他占用0x00000008 - 0x00000009.又由于有效对齐值圆整的关系,还有在short后面补齐两个字节,所以sizeof(struct t1)为12.同理sizeof(struct t1)为就为8了。从这,可以看出,我们合理定义变量值的顺序是可以节省内存空间的。


说到这你不禁想问,你怎么总是假设从0x00000000分配,从0x000000001开始分配,那么sizeof(struct t1)可能的值就不同了啊。其实,问这个的话,我还真没有办法解释,也没找到相关资料,只能说说我自己实验出来的一些东西。假如你的结构体元素长度最长的是char或者short,第一个元素的起始地址为必为D%4 == 2;若最长为int,那么其实地址D%4 == 0;若为float或者double,结构体的起始地址D%8 == 0。(当然,我用的是32位机,gcc的编译环境)。


看到这里了,我想还有一个问题得说说,相对与对齐值是数据类型自身属性而言,我们可以改变指定对齐值。那便是#pragma pack(n);n的值。
比如:
#pragma pack(1)
struct t2
{
	char a;
	int b;
	short c;
};
#pragma pack()
int main(void)
{
	printf("%d\n",sizeof(struct t2));
	return 0;
}


那么输出值就是7了。




3.哪些地方可以要设置对齐。
在设计不同CPU下的通信协议时,或者编写硬件驱动程序时寄存器的结构这两个地方都需要按一字节对齐。即使看起来本来就自然对齐的也要使其对齐,以免不同的编译器生成的代码不一样


好了,说到这,我已经黔驴技穷了,不过在找资料时看到了一个号称为intel和微软的笔试题目,和大家分享一下:

#pragma pack(8)
struct s1
{
	short a;
	long b;
};

struct s2
{
	char c;
	struct s1 d;
	long long e;
};
#pragma pack()

问 
1.sizeof(s2) = ?
2.s2的c后面空了几个字节接着是d?

理论上来说是24,但是gcc竟然是20(可能还有些问题没有考虑到,希望大家提点建议),不过还好vc上是24。

这是为什么呢。原来,gcc的最大pragma是4,所以,你懂的。sizeof(s2)就变成了20.好了,文章到这也就差不多结束了。加入你对我的博文有什么意见或者看法,欢迎留言的。

推荐博文:http://blog.csdn.net/21aspnet/article/details/6729724(讲得非常详细)。

你可能感兴趣的:(内存,数据结构,c/c++)