Chinaunix首页 | 论坛 | 博客
  • 博客访问: 167897
  • 博文数量: 27
  • 博客积分: 533
  • 博客等级: 中士
  • 技术积分: 426
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-14 19:54
文章分类

全部博文(27)

文章存档

2013年(4)

2012年(18)

2011年(5)

分类: LINUX

2012-10-05 12:47:30

一,内存地址对齐的概念

   计算机内存中排列、访问数据的一种方式,包含基本数据对齐和结构体数据对齐。

   32位系统中,数据总线宽度为32,每次能够读取4字节数据。地址总线为32,最大寻址空间为4GB。但是由于最低位A[0]~A[1]是不用于寻址的,因此只能访问4的倍数的地址空间,但是寻址空间还是2^30*字长=4GB。

  因此内存中除了结构体中成员变量之外的基本类型的开始的手地址最低两位都是0。基本类型数据对齐就是数据在内存中的偏移地址必须是一个字的倍数,以提高读取数据时的性能。为了对齐数据,必须在上个数据结束和下个数据开始处插入一些字节,这就是结构体数据对齐。

二,不进行对齐的影响

  例如int a的地址是0x00fffff3,则其字节分布在0x00fffff3~0x00fffff6空间内,为了读取这个int,cpu必须对 0x00fffff0和0x00fffff4进行两次内存读取,并处理得出的中间结果。两次内存访问将会浪费大量的时间,因为内存访问的速度远小于CPU 处理指令的速度。

三,结构体的内存地址对齐 

  结构体本身必须是4字节对齐的,而其成员变量则处理规则如下。

 以下是Microsoft和GNU对x86架构32位系统的结构体成员的默认对齐方式:

  char  1字节对齐

  short  2字节对齐

  int      4字节对齐

  float  4字节对齐

 double windows是8字节对齐,linux是4字节对齐

  当某一个成员后边的成员变量要求的地址对齐较大,则应该填入一些字节。且总的结构体大小为最大对齐的倍数,因此最后可能还要填充一些字符。

   因为上述结构体对齐的原因,将结构体成员按照大小递增/递减方式排序,可以减少结构体占用的空间大小。而这样同时使得对整个结构体的存取的效率变高了(占用小,整个的访问次数可以降低)。

  另外一个提高效率的方法是把一些占用字节数较少的成员合并到字节数占用大的成员,形成union类型。

四。准则

其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:

1. 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

2. 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;

3. 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

五.基本概念

字节对齐:计算机存储系统中以Byte为单位存储数据,不同数据类型所占的空间不同,如:整型(int)数据占4个字节,字符型(char)数据占一个字节,短整型(short)数据占两个字节,等等。计算机为了快速的读写数据,默认情况下将数据存放在某个地址的起始位置,如:整型数据(int)默认存储在地址能被4整除的起始位置,字符型数据(char)可以存放在任何地址位置(被1整除),短整型(short)数据存储在地址能被2整除的起始位置。这就是默认字节对齐方式。

六、结构体长度求法

1.成员都相同时(或含数组且数组数据类型同结构体其他成员数据类型):
结构体长度=成员数据类型长度×成员个数(各成员长度之和);
结构体中数组长度=数组数据类型长度×数组元素个数;

2.成员不同且不含其它结构体时;
(1).分析各个成员长度;
(2).找出最大长度的成员长度M(结构体的长度一定是该成员的整数倍);
(3).并按最大成员长度出现的位置将结构体分为若干部分;
(4).各个部分长度一次相加,求出大于该和的最小M的整数倍即为该部分长度
(5).将各个部分长度相加之和即为结构体长度

3.含有其他结构体时:
(1).分析各个成员长度;
(2).对是结构体的成员,其长度按b来分析,且不会随着位置的变化而变化;
(3).分析各个成员的长度(成员为结构体的分析其成员长度),求出最大值;
(4).若长度最大成员在为结构体的成员中,则按结构体成员为分界点分界;
其他成员中有最大长度的成员,则该成员为分界点;
求出各段长度,求出大于该和的最小M的整数倍即为该部分长度
(5).将各个部分长度相加之和即为结构体长度

七、空结构体 struct S5 { }; sizeof( S5 ); // 结果为1

“空结构体”(不含数据成员)的大小不为0,而是1。试想一个“不占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是,“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。

八、有static的结构体 struct S4{ char a; long b; static long c; //静态 };

静态变量存放在全局数据区内,而sizeof计算栈中分配的空间的大小,故不计算在内,S4的大小为4+4=8。

#pragma pack(n)指令设置1.2.4对齐。linux下最高位4.#pragma pack()指令默认为四。

九.union

union的长度取决于其中的长度最大的那个成员变量的长度。即union中成员变量是重叠摆放的,其开始地址相同。

其实union(共用体)的各个成员是以同一个地址开始存放的,每一个时刻只可以存储一个成员,这样就要求它在分配内存单元时候要满足两点:  
  1.一般而言,共用体类型实际占用存储空间为其最长的成员所占的存储空间;  
  2.若是该最长的存储空间对其他成员的元类型(如果是数组,取其类型的数据长度,例int   a[5]为4)不满足整除关系,该最大空间自动延伸;  
  我们来看看这段代码:   

union mm{ char a;//元长度1 int b[5];//元长度4 double c;//元长度8 int d[3]; };

本来mm的空间应该是sizeof(int)*5=20;但是如果只是20个单元的话,那可以存几个double型(8位)呢?两个半?当然不可以,所以mm的空间延伸为既要大于20,又要满足其他成员所需空间的整数倍,即24   
所以union的存储空间先看它的成员中哪个占的空间最大,拿他与其他成员的元长度比较,如果可以整除就行

 

阅读(10621) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~