结构体在内存中的对齐规则

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

例一:

#include <iostream>
using namespace std;
struct X
  {
	  char a;
      int b;
      double c;
  }S1;

void main()
 {
	 cout << sizeof(S1) << endl;
	 cout << sizeof(S1.a) << endl;
     cout << sizeof(S1.b) << endl;
     cout << sizeof(S1.c) << endl;
 }

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

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

比如此例,首先系统会将字符型变量a存入第0个字节(相对地址,指内存开辟的首地址);然后在存放整形变量b时,会以4个字节为单位进行存储,由于第一个四字节模块已有数据,因此它会存入第二个四字节模块,也就是存入到4~8字节;同理,存放双精度实型变量c时,由于其宽度为8,其存放时会以8个字节为单位存储,也就是会找到第一个空的且是8的整数倍的位置开始存储,此例中,此例中,由于头一个8字节模块已被占用,所以将c存入第二个8字节模块。整体存储示意图如图1所示。
在这里插入图片描述
考虑另外一个实例。

例二:

  struct X
    {
        char a;
        double b;
        int c;
    }S2;

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

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

例二中,我们分析完后的存储长度为20字节,不是最宽元素长度8的整数倍,因此将它补齐到8的整数倍,也就是24。这样就没问题了。其存储示意图如图2所示。

在这里插入图片描述

掌握了这两个原则,就能够分析所有数据存储对齐问题了。再来看几个例子,应用以上两个原则来判断。

例三:

   struct X
      { 
          double a;
          char b;
          int c;     
      }S3;

首先根据原则一来分析。按照定义的顺序,先存储double型的a,存储在第0-7个字节;其次是char型的b,存储在第8个字节;接下来是int型的c,顺序检查后发现前面三个四字节模块都被占用,因此存储在第4个四字节模块,也就是第12~15字节。按照第一原则分析得到16个字节,16正好是最宽元素a的宽度8的整数倍,因此结构体变量S3所占存储空间就是16个字节。存储结构如图3所示。
在这里插入图片描述
例四:

  struct X
       { 
          double a;
          char b;
          int c;
          char d;   
       }S4;

仍然首先按照第一原则分析,得到的字节数为8+4+4+1=17;再按照第二原则补齐,则结构体变量S4所占存储空间为24。存储结构如图4所示:
在这里插入图片描述
例五:

  struct X
    { 
          double a;
          char b;
          int c;
          char d;
          int e; 
      }S5;

同样结合原则一和原则二分析,可知在S4的基础上在结构体内部变量定义最后加入一个int型变量后,结构体所占空间并未增加,仍为24。存储结构示意图如图5所示。
在这里插入图片描述

例六:

如果将例五中加入的变量e放到第一个定义的位置,则情况就不同了。结构体所占存储空间会变为32。其存储结构示意图如图6所示。

 struct X
   { 
     int e;
     double a;
     char b;
     int c;
     char d;
   }S6;

在这里插入图片描述
补充:前面所介绍的都是元素为基本数据类型的结构体,那么含有指针、数组或是其它结构体变量或联合体变量时该如何呢?

1.包含指针类型的情况。只要记住指针本身所占的存储空间是4个字节就行了,而不必看它是指向什么类型的指针。

例七:

                struct X              struct Y               struct Z

                {                     {                      {     

                   char *a;              int *b;                 double *c;

                };                     };                     };

经测试,可知sizeof(X)、sizeof(Y)和sizeof(Z)的值都为4。

2.含有构造数据类型(数组、结构体和联合体)的情况。首先要明确的是计算存储空间时要把构造体看作一个整体来为其开辟存储空间;其次要明确的是在最后补齐时是按照所有元素中的基本数据类型元素的最长宽度来补齐的,也就是说虽然要把构造体看作整体,但在补齐的时候并不会按照所含结构体所占存储空间的长度来补齐的(即使它可能是最长的)。

例八:

struct X
 {
	char a;
    int b;
    double c;
 };
 
struct Y
{
   char a;
   X b;
};

经测试,可知sizeof(X)为16,sizeof(Y)为24。即计算Y的存储长度时,在存放第二个元素b时的初始位置是在double型的长度8的整数倍处,而非16的整数倍处,即系统为b所分配的存储空间是第8~23个字节。

如果将Y的两个元素char型的a和X型的b调换定义顺序,则系统为b分配的存储位置是第0~15个字节,为a分配的是第16个字节,加起来一共17个字节,不是最长基本类型double所占宽度8的整数倍,因此要补齐到8的整数倍,即24。测试后可得sizeof(Y)的值为24。

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

练习:
A. 补全/对齐

例1:答案是10吗?

struct Example {  
    int a;
    char b, c;
    int d;
};

不是,是12。为什么呢?因为结构体里的所有变量需要根据字节数量最多的那个数量对齐,比如这里最多是4(int),两个char虽然只需要占用2个字节,但对齐后它们就需要占用4个字节了,4*3=12。
那为什么要对齐呢?(据说是因为对齐了的话,read和write很好定位,不用麻烦去计算偏移量)

例2:不难算出下面的结果了吗?

struct Example {  
    int a;
    char b;
    int d;
    char c;
};

例3:再算下这个~

struct Example {  
    char b, c;
    long long e;
};

答案:例2(16),例3(16)
B. 嵌套结构体

如果只看了A的话,恐怕看到下面这个例子就会直接懵逼了:
例4:

struct Example {
    int a;
    long long e;

    struct SubStruct {
        int a;
        char ch;
        int b;
        char ch2;
        int d;
    } sub;
};

答案是40,为什么呢?
首先不看SubStruct,结果是4+8=12,int需要向long long对齐,所以4被扩展成8,最终结果是16,这个没问题。
然后看一下SubStruct里面的,也可以简单算出SubStruct的结果应该是20,那为什么最后的结果不是16+20=36,而是40呢?
还是对齐的原因,SubStruct结构体这个成员sub,也是外层结构体的一个成员,它的size也需要向“最大的元素”对齐,20向8对齐,是24,所以最终结果是16 + 24 = 40。

再看一个例子,保证很多人也会直接懵逼,hhh。
例5:

struct Example {
    int a;
    struct SubStruct {
        int a;
        char ch;
        int b;
        long long c;
    } sub;
    };

结果是32,why???
首先外层只有一个int,暂时认为是4,再看sub里面的,算出来应该是24,没错吧?将4对齐到8
8 + 24 = 32?
还是对齐的原因,这里的“最大”,应该理解为所有类型中的最大者,无论它是否在嵌套的struct里。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值