怎么计算struct结构所占空间?

        C语言结构体struct所占空间的计算一直是公司笔试题中的热点。

        我们来看一下每种情况下的结构体的大小:

一、结构体成员是基本类型

        总体上遵循两个原则:

       (1)、整体空间是占用空间最大的成员(的类型)所占字节数的整数倍。

       (2)、数据对齐原则---内存按结构体成员的先后顺序排列,当排到该成员时,其前面已摆放的空间大小必须是该成员类型大小的整数倍,如果不够则补齐,依次向后类推。
   
         某台机器是32位,即这个机器上char 占1个字节,int占4个字节,double占8字节

         我们举个栗子:
         struct  A
         {
                 char  a;
                 double  b;
                 int  c;
                 char  d;
         };
        
         那么,这个结构体占用多少字节呢?

         <1>、它的第一个成员是char 类型的 a,占 1 个字节。放入结构的0地址处:
         <2>、下来要存储的是第二个成员是double 类型的 b,占 8 个字节。它该怎么存放呢?
                   我们回想到原则(2)中说,存储到某个成员时,前面已经存放的所有成员所占的总空间大小是改成员大小的整数倍,不够则补齐!
                   也就是<1>图中显示的下一个存储成员起始地址应该是 8 的整数倍,而上图中显示的起始地址为 1 ,并不是 8 的整数倍,所以需要补齐。向后增加地址数到 8 (补齐操作),这时 8 是 8 的整数倍,可以存储。则将成员 b 存放到 8 地址处:
          <3>、下来要存储的是第三个成员是 int 类型的 c,占 4 个字节,由上图所知,下一个存储成员的起始地址是16,刚好是 4 的倍数。所以不用补齐,直接用 4 个字节来存储成员 c:
         <4>、最后存储的是第四个成员是 char 类型的 d,占 1 个字节。由上图所知,下一个存储成员的起始地址是20,刚好是 1 的倍数。所以不用补齐,直接用 1 个字节来存储 成员 d:
       <5>、把所有成员都存储到位,这就完了吗?
                 No!我们还要考虑最后一个要素:原则(1),整体空间是占用空间最大的成员(的类型)所占字节数的整数倍!而这个结构的占用空间最大的成员是 double 类型的 b,占用 8 个字节。而现在整体空间由上图所知是 21 个字节,并不是 8 的倍数,所以仍需要补齐!补多少呢? 比 21 大的最小的 8 的倍数是 24,所以就补到 24 字节:
           所以最终算下来,结构体 A 的大小是 24字节!

           最后,我们再来用实际代码测试一下:

#include <stdio.h>

struct A
{
	char a;
	double b;
	int c;
	char d;
};

int main(void)
{
	printf("sizeof(char) = %d\n", sizeof(char));
	printf("sizeof(int) = %d\n", sizeof(int));
	printf("sizeof(double) = %d\n\n", sizeof(double));

    printf("sizeof(struct A) = %d\n", sizeof(struct A));
	return 0;
}

运行截图:
                           



二、结构体成员是另外一个结构体时


        之前的两个原则将做一些修改,我们把本结构体称为父结构体,把成员结构体变量称为子结构体:

       (1)、整体空间是子结构体与父结构体中占用空间最大的成员(的类型)所占字节数的整数倍。

       (2)、数据对齐原则---父结构体内存按结构体成员的先后顺序排列,当排到子结构体成员时,其前面已摆放的空间大小必须是该子结构体成员中最大类型大小的整数倍,如果不够则补齐,依次类推。
   
         某台机器是32位,即这个机器上char 占1个字节,int占4个字节,double占8字节
 
         结构体例子:
          struct  A
         {
                 char  a;
                 double  b;
                 int  c;
                 char  d;
         };

         struct  B
         {
                 char a;
                 struct  A  b;
                 int  c;
         };

         那么,结构体 B 占多少字节呢?通过之前分析,我们已知结构体 A 的大小为 24 字节:

         <1>、首先存储成员 a ,占用 1 个字节。下一个存储地址是 1

         <2>、其次存储成员 b,而 b 是结构体A的变量,结构体A中最大类型大小double,占8 个字节,所以存储该变量地址应是 8 的倍数,所以需要补齐地址到 8 ,然后存储大小为 24 字节的变量 b:
         <3>、最后存储成员 c,成员 c 类型是 int,占用 4 个字节。由上图可知 c 的存储始地址是 32 ,刚好是 4 的倍数,并不用补齐,所以直接存储:
         <4>、最后也要看整体空间大小是否满足原则(1)
                   我们发现,当前结构体总大小是 36 ,而子结构体和父结构体中最大类型是double,占 8 个字节。而 36 并非是 8 的整数倍,所以需要补齐为 8 的整数倍。即  40 字节:
           所以最后,结构体B的大小是 40 字节。
     
           实例验证:

        

三、结构体成员是数组类型

       之前的理解了,那么这个也就不难理解!
       只要不把数组看作数组,而把它看成一个个类型相同的成员变量序列!然后用前面的计算方法计算即可。

四、包含位域成员的结构体大小计算
       
        使用位域的主要目的是压缩存储,其大致规则为:
        (1)、如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止。
        (2)、如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍。
        (3)、如果相邻的位域字段的类型不同,则各编译器的具体实现有差异。VC6采取不压缩方式,gcc采取压缩方式。
        (4)、如果位域字段之间穿插着非位域字段,则不进行压缩。
        (5)、整个结构体的总大小为最宽的基本类型成员大小的整数倍。
       
        举个栗子:

        (1)在Linux + gcc 环境下,以下结构体的sizeof值是多少?
 
        struct  A
        {
               int  f1 : 3;
               char  b;
               char  c;
        }
 
        解析: sizeof( struct A ) 的值为 4, 由于 f1 只占用了 3 个位,不足一个字节,假设 f1 摆放在地址 0 处,则地址1可用,故 b 摆放在地址 1 处,则 c 摆放在 2 处, 0 ~ 2 共 3 个字节不是 int 的整数倍(整个结构体的总大小为最宽基本类型成员int大小的整数倍),故地址 3 处补齐,共四个字节。如下图:

            如果在VC环境下,sizeof(struct A)的值为 8!

       (2)、
        
         struct  B
         {
                char  f1 : 3;
                char  f2 : 4;
                char  f3 : 5;
         }
 
         解析: 按照之前规则,第一个字节仅能容下f1 和 f2,所以 f2 被压缩存储到 第一个字节中,而f3 只能从下一个字节开始存储,所以sizeof( struct B )的值为 2 ;如下图:
          (3)、相邻位域类型不同的情况
           struct  C
           {
                  char  f1:3;
                  short  f2 : 4;
                  char f3 : 5;
           }
           解析: 由于相邻位域类型不同,在VC中不压缩,所以sizeof值为 6 ,而在gcc中压缩,其值为 2 。

          (4)、
           
           struct  D
           {
                  char  f1:3;
                  short  f2 : 4;
                  char f3 : 5;
           }
           解析:非位域字段穿插其中,不会产生压缩,在VC和gcc中sizeof大小均为 3 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章