结构体内存对齐

        最近,在做题时遇到了结构体内存对齐的问题,发现这是我的一个盲点。于是上网搜集资料补一下这方面的知识。

        结构体是一种数据结构,其可以容纳多种不同的数据类型。但是对于这些数据类型,其有一点要求,就是要求数据内存对齐。也就是说,结构体的内存对齐不是对所有数据类型所占的空间字节数的简单相加,而是允许有一定的浪费,但是这样做却换来了访问数据的高效性,也就是俗称的以空间换时间。

        那么结构体中的数据成员是怎样对齐的呢?对于结构体的成员,每个成员都以自己所占字节数的整数倍作为偏移地址的开始,然后按照自己的所占字节数进行占位。最后还要注意一点,结构体的大小一定是最大数据成员内存大小的整数倍,也就是说,如果算出来的大小不满足这个条件,会自动补充空间。

        仅仅说文字总感觉还是有点难以理解,还是来看个例子吧

struct S1
{
    char c1;
    int a;
    char c2;
};

struct S2
{
    char c1;
    char c2;
    int a;
};
       在x86  32位机上运行,可以看到他们的结果sizeof(S1) = 12,  sizeof(S2) = 8.如下图

      对于这两个结构体,其数据成员是一样的,只是声明的顺序不一样,却造成了其大小不一样的结果。现在我们来分析一下为什么会出现这种结果

       对于结构体S1,第一个是char类型,占一个字节,其开始的地方的偏移量必须是1的整数倍,这里我们假设结构体从0地址开始(下同),所以char 就占据了第一个字节;第二个类型是int类型,占4个字节,则int开始的地方的偏移量必须是4的整数倍,由于第一个字节位置已经被第一个char类型占据,因此int类型开始的地方的偏移量不是0*4 = 0,而是1*4 = 4,也就是说第5~8个字节是放int类型的,那么2~4字节的位置怎么办,就空在那里了。接下来是第三个类型char类型,占1个字节,其开始的位置的偏移量必须是1的整数倍,由于int类型结束时是字节为8的位置,所以第9个字节的地方放第二个char类型。其图示如下图

        对于结构体S2,第一个是char类型,占一个字节,其开始的地方的偏移量是1的整数倍,因此第一个char占据第一个字节;第二个数据成员还是char类型,其开始的地方的偏移量是1的整数倍,由于第一个字节已被占据,其只能从第二个字节(2是1的倍数)开始,因此第二个char类型占据第二个字节;第三个数据成员是int类型,其大小是4个字节,其开始的地方是偏移量为4的整数倍的位置,由于偏移量0*4 = 0处开始的字节已经被占据,而偏移量1*4 = 4开始的位置并未被占据,因此第三个int类型占据5~8的字节。由于此时8是4(结构体中最大数据成员的空间大小,也就是int的大小)的倍数,所以不需要补充多余的空间。所以第二个结构体的空间大小是8,其图示如下图

        说完了简单的情况,再来说一点稍微复杂的情况,就是结构体中含有数组的情况。看如下代码

struct S
{
	char a; 
	int b[2];
	char c;
};
       那么sizeof(S) = 24 还是 sizeof(S) = 16?答案是16。如下图

     

        原因是结构体中的数组并不是作为一个整体对齐,而是对数组中每个成员分开分别对齐。在此例中,第一个数据成员char占据第一个字节,第二个数据成员是一个包含两个数的数组,那么拆开来就是两个int数据类型,因此第2~4字节留空以让int类型对齐,而数组则占据第5~12字节,最后的char类型占据第13个字节,最后在填充第14~16个字节以满足结构体整体大小是结构体最大数据成员大小的整数倍的要求。其图示如下


        最后,再说一种情况,就是结构体中含有结构体的情况。假设存在以下代码

struct S1
{
    int a;       //  4 bytes
    double d1;   //  8 bytes
};

struct S2
{
    char c1;    //  1 byte
    char c2;    //  1 byte
    S1 sss;     //  16 bytes
    int a;      //  4 bytes
    char c3;    //  1 byte
};

       对于结构体S1,可以很快的判断出来其大小是16个字节,具体分析参考上面。那么结构体S2的大小是多少呢?是不是按照上面的分析来等于48个字节呢?从运行结果我们可以看出其大小只有32个字节,如下图所示,那是什么原因呢?

       原来结构体作为数据成员存在于另一个结构体中时,其在算最大的数据成员空间大小时是会分开与其他数据成员(包括自身结构体的数据成员和包含此结构体的外面的结构体中的其他成员)进行比较,然后找出最大的值,以此来进行填充空白的空间以保证对齐;然而,在计算结构体大小时,其内嵌的结构体作为一个数据成员,是按该内嵌结构体的大小来计算的,但是却不会按照内嵌结构体的整体大小来对齐,而是按照内嵌结构体的最大数据成员的空间大小来对齐。针对上面这种情况,S2中含有结构体S1,其展开后与S1的所有数据类型中double所占空间最大,因此最后结构体S2的大小必然是8的倍数,而在计算结构体S2大小的过程中,第一个第二个char数据类型分别占据第一个第二个字节,而对于第三个数据成员S1,虽然S1是作为整体的数据类型占据16个字节,但是并不是16个字节来对齐的,按照S1的最大数据成员的空间大小(此处是sizeof(double) = 8)来对齐,因此第3~8自己留空,第9~24字节被S1占据;接着最后一个数据类型char,占据第25个字节,最后为了满足整体空间大小是最大数据成员大小的整数倍的要求,在最后填充第26~32个字节空白空间。其图示如下所示


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值