struct的对其规则

来源:互联网 发布:逻辑思维导图软件 编辑:程序博客网 时间:2024/04/30 06:49

对齐:

现代计算机中内存空间都是按着byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就是需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

对齐的作用:

各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存储。其他平台可能没有这种情况,但是最常见的是如果不按照合适其平台的要求对数据进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶数地址开始,如果一个int型(假设为32位)如果存放在偶数开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次独处的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。

对齐的实现

通常我们写程序的时候,不需要考虑对齐问题,编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对制定数据的对齐方法。缺省情况下,编译器为结构体的每个成员按其自然対界条件分配空间。各个成员按照他们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。自然対界即默认对齐方式,是指按结构体的成员中size最大的成员对齐。

最常见的就是struct数据结构的sizeof的结果出乎意料。

结构体的sizeof的值并不是简单的将其中各个元素所占的字节相加,而是要考虑到存储空间的字节对齐问题

结构体默认的字节对齐准则:

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

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

3.          结构体的总大小为结构体最宽基本类型成员大小和编译器缺省対界条件大小中比较小得那个值的整数倍,如有需要编译器会在最后一个成员之后加上填充字节;

一 结构体长度的求法:

A.    成员都相同时(或含数组且数组数据类型同结构体其他成员数据类型)

结构体长度=成员数据类型长度*成员个数;

结构体中数组长度=数组数据类型长度*数组元素个数;

B.    成员不同

第一步: 结构体的首地址有系统自动分配我们不予考虑

第二步:计算第一个成员变量的大小

第三步:计算第二个成员变量的大小,此时要注意此成员变量的偏移量(距离结构体首地址的长度)要保证是此变量大小的整数倍,如果不够则补空位;

依次计算所有成员变量,并求和。

第四步:选出所有成员变量中长度最长的变量的值,此时要保证总和是此变量长度的整数倍。如果不是则在最后面补空位

注意:结构体作为成员时,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。

 

例题一、

struct  test1

{

char a;

int  b;

double c;

bool d;

};

则sizeof(test1)值为24

内存结构为 1***  1111  11111111 1*******(其中*为补空位)

例题二、

struct  test2

{

        char a;

        struct test1  bb;

        int c;

};

则sizeof(test2)的值为40

首先求a大小为1,在求bb时我们需要考虑偏移量,此时我们使用的bb的对比值并不是24而是test1中的最长值8,因此在字符a后需要补空位7位

然后加上bb长度24,

再计算c并加上其长度4.此时一共长36.

最后我们要注意原则中的第三条。在test2中最长的是结构体类型bb中的double,故总长度应该是8的整数倍。所以最后补位4位,得到40

内存结构为 1********  1***  1111  11111111  1******* 1111****(其中*为补空位)

例题三、

Structtest3

{

        Char a;

        Int b[4];

};

此时计算sizeof(test3)为20

 

内存结构为1***  1111 1111 11111111

二、对齐规则

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数32位机上是8)。程序员可以通过预编

译命令#pragmapack(n),n=1,2,4,8,16 来改变这一系数,其中的n就是你要指定的“对齐系数”。

 

指定対界:

一般的,可以通过下面的方法来改变缺省的対界条件:

使用伪指令#pragma pack(n),编译器将按照n个字节对齐;

使用伪指令#pragma pack(),取消自定义的字节对齐方式;

注意:如果#pragma pack(n)中指定的n大于结构体中最大的成员的size,则其不起作用,结构体仍然按照size最大的成员进行対界。

 

规则1:

数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset

为0 的地方,以后每个数据成员的对齐按照#pragmapack 指定的数值和这个数据成员自身长度中,比较小的那个进行。

规则2:

结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进

行对齐,对齐将按照#pragmapack 指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

规则3:

结合1、2 颗推断:当#pragma pack的n 值等于或超过所有数据成员长度的时候,这个n

值的大小将不产生任何效果。

试验

我们通过一系列例子的详细说明来证明这个规则吧!

我试验用的编译器包括GCC 3.4.2 和VC6.0的C 编译器,平台为Windows XP +Sp2。

我们将用典型的struct对齐来说明。首先我们定义一个struct:

#pragma pack(n)

struct test_t

{

int a;

char b;

short c;

char d;

};

#pragma pack(n)

首先我们首先确认在试验平台上的各个类型的size,经验证两个编译器的输出均为:

sizeof(char) = 1

sizeof(short) = 2

sizeof(int) = 4

我们的试验过程如下:通过#pragmapack(n)改变“对齐系数”,然后察看sizeof(struct test_t)的值。

1、1 字节对齐(#pragmapack(1))

输出结果:sizeof(structtest_t) = 8 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(1)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=8

2) 整体对齐

整体对齐系数=min((max(int,short,char), 1) = 1

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整= 8[注1]

2、2 字节对齐(#pragmapack(2))

输出结果:sizeof(structtest_t) = 10 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(2)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=min((max(int,short,char), 2) = 2

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整= 10

3、4 字节对齐(#pragmapack(4))

输出结果:sizeof(structtest_t) = 12 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(4)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=min((max(int,short,char), 4) = 4

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整= 12

4、8 字节对齐(#pragmapack(8))

输出结果:sizeof(structtest_t) = 12 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(8)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=min((max(int,short,char), 8) = 4

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整= 12

5、16 字节对齐(#pragmapack(16))

输出结果:sizeof(structtest_t) = 12 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(16)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=min((max(int,short,char), 16) = 4

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整= 12 

[注1]

什么是“圆整”?

举例说明:如上面的8 字节对齐中的“整体对齐”,整体大小=9 按4 圆整= 12

圆整的过程:从9 开始每次加一,看是否能被4 整除,这里9,10,11 均不能被4 整除,到12 时可以,则圆整结束。



http://blog.sina.com.cn/s/blog_6877dad30100wxpn.html









struct/class以及union内存对齐四个原则:

1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节, 则要从4的整数倍地址开始存储),基本类型不包括struct/class/uinon。

2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部"最宽基本类型成员"的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。

3、收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的"最宽基本类型成员"的整数倍.不足的要补齐.(基本类型不包括struct/class/uinon)。

4、sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。

 

 

测试用例:

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3. union a  
  4. {  
  5.  int a_int1;  
  6.  double a_double;    //8字节  
  7.  int a_int2;  
  8.  char ch;  
  9.                      //由原则4,a的大小为8  
  10. };  
  11. struct b  
  12. {  
  13.  a a1;              //[0]...[7] ,  
  14.  char y;            //[8]  
  15.                     //由原则3,"最宽基本类型成员"为a中的double,从而b的大小为8的整数倍,所以b的大小为16  
  16. };  
  17. class c  
  18. {  
  19.  int c_double;      //[0]...[4]  
  20.  b b1;              //[7]...[23]   原则2:与b的内部的最大元素的整数倍开始存储  
  21.  char ch;           //[24]  
  22.                     //由原则3,"最宽基本类型成员"为b中,即b里面a的double,c的大小为8的整数倍,所以c的大小为32  
  23. };  
  24. struct usc  
  25. {  
  26.     char ch;        //[0]  
  27.     b b2;           //[7]...[23]   原则2:与b的内部的最大元素的整数倍开始存储  
  28.     a a2;           //[24]...[31]     
  29.     c c2;           //[32]...[63]  
  30.     char ch1;       //[64]  
  31.                     //"最宽基本类型成员"为a中的double 或者 即b里面a的double 或者 c里面的b里面的double  
  32.                     //由原则3,usc的大小为8的整数倍,所以usc的大小为72  
  33. };  
  34. void main()  
  35. {  
  36.     cout<<"sizeof(a)= "<<sizeof(a)<<endl;  
  37.     cout<<"sizeof(b)= "<<sizeof(b)<<endl;    
  38.     cout<<"sizeof(c)= "<<sizeof(c)<<endl;                                               
  39.     cout<<"sizeof(usc)= "<<sizeof(usc)<<endl;    
  40. }  
 

总结:

       从“struct/class以及union内存对齐原则”可以得出:在struct/class/union中定义变量时,长度小的变量先定义,长度大的变量后定义,可以节省内存。

 

参考:1.http://blog.csdn.net/hairetz/archive/2009/04/16/4084088.aspx

         2.http://blog.csdn.net/yuesheng3707/archive/2009/10/23/4715721.aspx


原创粉丝点击