Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1922597
  • 博文数量: 261
  • 博客积分: 8073
  • 博客等级: 中将
  • 技术积分: 2363
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-10 15:23
文章分类

全部博文(261)

文章存档

2013年(1)

2012年(1)

2011年(50)

2010年(34)

2009年(4)

2008年(17)

2007年(55)

2006年(99)

分类:

2008-07-02 07:40:32

    为了提高内存存取的效率,对于复杂数据结构(如结构体),其各个成员是按一定的规则在内存中分布的。这个规则就是“对齐”.
    对其分为两种:自然对齐和指定对齐。
    自然对齐是默认的对齐方式,按照结构体中size最大的成员进行对齐。
    指定对齐可以通过#pragma pack(n)指令来指定对齐的大小n,但注意:结构体中某个成员x的对齐大小为min(sizeof(x), n)。
 
    对齐有以下一些原则:
    1.结构体中每个成员x按照min(sizeof(x), n)进行对齐。
    2.结构体中某个成员x当发现x之前的内存分布不是min(sizeof(x), n)的整倍数时,x需要在"x之前的内存分布"与"x"之间填充空字节以保证"x之前的内存分布"是min(sizeof(x), n)的整倍数。但注意:补充空字节后,x之前的内存必须保证其"最短性原则"。比如,x之前的内存为3字节,而min(sizeof(x), n) = 2,因此可以在x之前补充1,3,5...个空字节,都可以满足要求,但是为了保持最短性,只能选择补充1个空字节。
    3.结构体的总大小必须是min(max(结构体中每个成员的大小sizeof),n)的整倍数。如果不满足要求,则需要补充空字节,补充的空字节数量同原则2一样也需要满足"最短性原则"。
 
    注意:以上假设指定了#pragma pack(n),在没有指定#pragma pack(n)的情况下,可以将以上三条原则中的n当成0来处理
 
下面举两个例子:
   

struct test{
     char a;
     char b;
     char c;
     short int d;
     int e;
     char f;
};

由于没有指定#pragma pack(n),因此该结构体按照自然对齐方式。

a按照1字节对齐。当前内存分布为

1

b按照1字节对齐,同时b之前的内存分布是1字节的整倍数。当前内存分布为:

1,1

c按照1字节对齐,同时b之前的内存分布是1字节的整倍数。当前内存分布为:
1,1,1

d按照2字节对齐,同时b之前的内存分布不是2字节的整倍数,则需要在d之前补充空字节来保证d之前的内存是2字节的整倍数。当前内存分布为:

1,1,1*,11

e按照4字节对齐,同时e之前的内存分布不是4字节的整倍数,则需要在e之前补充空字节来保证e之前的内存是4字节的整倍数。当前内存分布为:
1,1,1*,11**,1111
f按照1字节对齐,同时f之前的内存分布是1字节的整倍数。当前内存分布为:
1,1,1*,11**,1111,1

但是注意目前结构体的大小(13)不是该结构体成员最大size(当前结构体其成员size最大的成员为e,size=4)的整倍数,因此需要补充3个空字节来。当前内存分布为:

1,1,1*,11**,1111,1***

注意:该例中","为分隔符,不占存储空间

 

 

下面有一道在 上讨论火热的题:

Intel和微软和本公司同时出现的面试题

#pragma pack(8)

struct s1{
short a;
long b;
};

struct s2{
char c;
s1 d;
double e;
};

#pragma pack()


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

以下结果在VC6.0下编译得到:

sizeof(S2)结果为24.
成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.
也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.
S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;
S2中,c和S1中的a一样,按1字节对齐,而d 是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是按4字节对齐.成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e按8字节对齐)整除.这样,一共使用了24个字节.
                          a    b
S1的内存布局:11**,1111,
                          c    S1.a S1.b     d
S2的内存布局:1***,11**,1111,****11111111

这里有三点很重要:
1.每个成员分别按自己的方式对齐,并能最小化长度
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度
3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐

对于数组,比如:
char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐.
如果写: typedef char Array3[3];
Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度.
不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个.

 

在gcc 3.4.4/4.1/4.2编译运行得到如下结果

sizeof(t_s2) = 20
&t_s2.c = 0xbfef1100
&t_s2.d = 0xbfef1104
&t_s2.e = 0xbfef110c

其中struct s2 t_s2

gcc中是如何处理嵌套结构体对齐的呢?

阅读(989) | 评论(0) | 转发(0) |
0

上一篇:I/O内存于I/O端口

下一篇:Linker Script简介

给主人留下些什么吧!~~