结构体的内存对齐

来源:互联网 发布:淘宝售后客服绩效考核 编辑:程序博客网 时间:2024/05/16 15:56

 结构体的内存对齐 一个结构体变量定义完之后,其在内存中的存储并不等于其所包含元素的宽度之和。

例一:

                                      #include <iostream>

                                      using namespace std;

                                         struct X

                                         {

                                              char a;

                                              int b;

                                              double c;

                                         }P1;

 

                                     void main()

                                    {

                                         cout << sizeof(P1) << endl;

                                         cout << sizeof(P1.a) << endl;

                                         cout << sizeof(P1.b) << endl;

                                         cout << sizeof(P1.c) << endl;

                                    }

     比如例一中的结构体变量S1定义之后,测试发现sizeof(P1)= 16,其值不等于sizeof(P1.a) = 1、sizeof(P1.b) = 4和 sizeof(P1.c) = 8三者之和,这里面就存在存储对齐问题。

    原则一:结构体中元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每一个元素放置到内存中时,它都会认为内存是以它自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始(以结构体变量首地址为0计算)。

    上例中,首先系统会将字符型变量a存入第0个字节;然后在存放整形变量b时,会以4个字节为单位进行存储,由于第一个四字节模块已有数据,因此它会存入第二个四字节模块,也就是存入到4~8字节;接着存放双精度实型变量c时,由于其宽度为8,其存放时会以8个字节为单位存储,也就是会找到第一个空的且是8的整数倍的位置开始存储,此例中,由于头一个8字节模块已被占用,所以将c存入第二个8字节模块。

例二:

                                           struct X

                                           {

                                                char a;

                                                double b;

                                                int c;

                                            }P2;

    在例二中仅仅是将double型的变量和int型的变量互换了位置。测试程序不变,测试结果却截然不同,sizeof(P2)=24,不同于原则一计算出的8+8+4=20,这就引出了第二原则。

    原则二:在经过第一原则分析后,检查计算出的存储单元是否为所有元素中最宽的元素的长度的整数倍,若是,则结束;否则,补齐为它的整数倍。

    例二中,我们分析完后的存储长度为20字节,不是最宽元素长度8的整数倍,因此将它补齐到8的整数倍,也就是24。


  掌握了这两个原则,就能够分析所有数据存储对齐问题了。再举例练习如下:

例:

                                              struct X

                                              { 

                                                   double a;

                                                   char b;

                                                   int c;

                                                   char d;   

                                              }P3;

 

    先按照第一原则分析,得到的字节数为8+4+4+1=17;再按照第二原则补齐,则结构体变量P3所占存储空间为24。但下例却不同,


例:

                                              struct X

                                              { 

                                                   double a;

                                                   char b;

                                                   int c;

                                                   char d;

                                                   int e; 

                                               }P4;

    同样结合原则一和原则二分析,可知在P3的基础上在结构体内部变量定义最后加入一个int型变量后,结构体所占空间并未增加,仍为24。


因为结构体所占空间不但与其内部元素的类型有关,而且与不同类型元素的排列有关。因此在定义结构体时,在元素类型及数量确定之后,我们还应该注意一下其内部元素的定义顺序。




位域:有些信息在存储时,并不需要占用一个完整的字节,而只需要占几个或一个二进制位。例如在存放一个开关变量时,只有0、1两种状态,用一位二进制位即可。为了节省存储空间,并使处理简单,C语言提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进制位划分为几个不同的区域,每个区域有一个域名,并指定每个区域的位数,允许在程序中按域名进行操作。

一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: 

struct 位域结构名 { 位域列表};

其中位域列表的形式为:

类型说明符 位域名位域长度

位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:

struct bs{  int a:8;  int b:2;  int c:6;}data; 

说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。

对于位域的定义有以下几点说明:

1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。

2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度

3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。

二、位域的对齐

  如果结构体中含有位域,准则是:

  1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

  2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

  3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++和GCC都采取压缩方式;

  系统会先为结构体成员按照对齐方式分配空间和填塞,然后对变量进行位域操作。








原创粉丝点击