Chinaunix首页 | 论坛 | 博客
  • 博客访问: 756021
  • 博文数量: 128
  • 博客积分: 7079
  • 博客等级: 少将
  • 技术积分: 1326
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-16 08:53
文章分类

全部博文(128)

文章存档

2011年(3)

2010年(12)

2009年(9)

2008年(23)

2007年(61)

2006年(20)

我的朋友

分类: C/C++

2010-11-09 11:28:35

一、什么是字节对齐

  一个变量占用 n 个字节,则该变量的起始地址必须能够被 n 整除,即: 存放起始地址 % n = 0 对于结构体而言,这个 n 取其成员种的数据类型占空间的值最大的那个。

二、为什么要字节对齐

  内存空间是按照字节来划分的,从理论上说对内存空间的访问可以从任何地址开始,但是在实际上不同架构的CPU为了提高访问内存的速度,就规定了对于某些类型的数据只能从特定的起始位置开始访问。这样就决定了各种数据类型只能按照相应的规则在内存空间中存放,而不能一个接一个的顺序排列。

  举个例子,比如有些平台访问内存地址都从偶数地址开始,对于一个int(假设32位系统),如果从偶数地址开始的地方存放,这样一个读周期就可以读出这个int数据,但是如果从奇数地址开始的地址存放,就需要两个读周期,并对两次读出的结果的高低字节进行拼凑才能得到这个int数据,这样明显降低了读取的效率。

三、如何进行字节对齐

3.1结构体字节对齐有两个必要条件:一、成员在结构体中相对于首地址的偏移量必须是自身对齐值(设这个值为X)的整数倍。二、结构体的大小必须是结构体对齐值(设这个值为Y)的整数倍。

成员自身对齐值X = min( 系统对齐值,成员类型大小 ) ,系统对齐值在VC中默认是8 ,也可以由语句设定,例如:

#pragma pack(value)

......

......

#pragma pack()

在这两句之间的系统对齐值被设置成为value

结构体对齐值取各成员自身对齐值中对大的,即Y = max( x1,x2,...... xn )  

3.2各个数据类型的大小:

数据类型   char  short   int   long   float   double   (long double)

gcc3.2.2   1       2       4     4        4        8             12
Visual C++: 1       2       4     4        4        8              8

数组的大小=数组长度X 数组成员类型大小 ,但当数组作为参数时,数组的大小应该为系统地址大小,因为此时传递的只是数组首地址。

void function  (int array[10])  { printf(\"%d\\n\",sizeof(array));   }
int main(int argc , char *argv[])
{      
   int array[10];
   function(array);
   return 1;

这里的输出结果是4,因为C语言在数组作为参数的时候传递的只是地址,也就是在function这个函数用到的array只不过是个指针变量,其结果返回是4,因为:在Win32平台上地址为32位即指针变量的大小为4字节。

共用体变量的所占字节数为最大数据类型成员的大小,例如 union Union { int i,char  ch}; Union test; test  的大小为4(int类型的大小)
    
枚举类型变量大小为4字节,因为枚举类型是派生自System.Enum的一种独特的值类型,用于声明一组命名的常数。每种枚举类型均有一种基础类型,此基础类型可以使除char类型以外的任何整型。

枚举元素的默认基础类型为int 默认情况下,第一个枚举元素的值为0,后面每个枚举元素的值依次递增1 。如

emum weekday{sun,mon,tue,wed,thu,fri,sat},在此枚举中,sun的值为0 mon1 以此类推。也可emum weekday{sun=1,mon,tue,wed=sun,thu,fri,sat},强制第一个枚举元素sun的值为1,mon2tue3,而wed又强制为1,依次类推。

如果枚举元素的数据类型不是int型,则可 enum color:long{red,green,blue}

例:使用枚举类型

using system;

public class testenum

{

enum range:long{max=2147483648L,min=255L}

public static void main()

{

long a=(long) range.max;

long b=(long) range.min;

console.writeline("max={0},min={1}",a,b);

}

}

  3.3字节对齐方法

  按照3.1中描述的两个条件,结构体在在内存的存放顺序用如下规则即可映射出来:

  ()每个成员的起始地址 % 每个成员的自身对齐值 = 0,如果不等于 0 则先补空字节直至这个表达式成立;

  ()结构体的长度必须为结构体的自身对齐值的整数倍,不够就补空字节。

举个例子:

#pragmapack(8)
structA{
  chara;
  longb;
};

对于 struct A 来说,对于char型数据,其自身对齐值为1,对于long类型,其自身对齐值为4, 结构体的自身对齐值取其成员最大的对齐值,即大小4。那么struct A 在内存中的顺序步骤为:

  (1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。

  (2) long b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0, 所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x0007.

  (3)此时成员都存放结束,结构体长度为8,为结构体自身对齐值的2倍,符合条件().

  此时满足条件()和条件()struct A 中各成员在内存中的位置为:a*** b ,sizeof(struct A) = 8(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位)


structB{
  chara;
  structAb;
  longc;
};

对于struct B,里面有个类型为struct A的成员b自身对齐值为4,对于long类型,其自身对齐值为4. struct B的自身对齐值为4。那么structB 在内存中的顺序步骤为:

  (1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。

  (2) struct A b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0, 所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x00011.

  (3) long c,地址起始位置从0x000012开始, 因为 0x0012 % 4 = 0,其地址范围为0x00012~0x0015.

  (4)此时成员都存放结束,结构体长度为16,为结构体自身对齐值的4倍,符合条件().

  此时满足条件()和条件()struct B 中各成员在内存中的位置为:a*** b c ,sizeof(struct C) = 16(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占四位)


structC{
  chara;
  structAb;
  doublec;
};

对于struct C,里面有个类型为struct A的成员b自身对齐值为4,对于double 类型,其自身对齐值为8. struct C的自身对齐值为8。那么struct C 在内存中的顺序步骤为:

  (1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。

  (2) struct A b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0, 所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x00011.

  (3) double c,地址起始位置不能从0x000012开始, 因为 0x0012 % 8 != 0,所以先补空字节,直到0x000015结束,即补4个字节的空字节,从0x00016开始存放c,其地址范围为0x00016~0x0023.

  (4)此时成员都存放结束,结构体长度为24,为结构体自身对齐值的3倍,符合条件().

  此时满足条件()和条件()struct C 中各成员在内存中的位置为:a*** b **** c ,sizeof(struct C) = 24(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占八位)


structD{
  chara;
  structAb;
  doublec;
  intd;
};
structE{
  chara;
  intb;
  structAc;
  doubled;  
};

  

  

  

  对于struct D,自身对齐值为8。前面三个成员与 struct C 是一致的。对于第四成员d,因为 0x0024 % 4 = 0, 所以可以从0x0024开始存放d, 其地址范围为0x00024~0x00027.此时成员都存放结束,结构体长度为2828 不是结构体自身对齐值8的倍数,所以要在后面补四个空格,即在0x0028~0x0031上补四个空格。补完了,结构体长度为32, 为结构体自

  身对齐值的4被,,符合条件().

  此时满足条件()和条件()struct D 中各成员在内存中的位置为:a*** b **** c d **** ,sizeof(struct D) = 32(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占八位, d占四位)

  对于struct E 中各成员在内存中的位置为:a*** b c d, sizeof(struct E) = 24(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位,c占八位, d占八位)

  通过struct D struct E 可以看出,在成员数量和类型一致的情况,后者的所占空间少于前者,因为后者的填充空字节要少。如果我们在编程时考虑节约空间的话,应该遵循将变量按照类型大小从小到大声明的原则, 这样尽量减少填补空间。另外,可以在填充空字节的地方来插入reserved成员, 例如

struct A
{
  char a;
  char reserved[3];
  int b;
}

  这样做的目的主要是为了对程序员起一个提示作用,如果不加则编译器会自动补齐。

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