结构体的内存对齐

之前我们都知道int类型大小是4个字节,char类型为1个字节,double为8个字节等等。

但是结构体是由很多类型组成的,那么它的大小是多少呢?是所有类型相加之和还是另有算法?

接下俩我们将仔细的探讨它.


  先来说结论:结构体大小不是所有类型大小的和,而是存在一种内存对齐的方式,使得结构体中类型按某种方式对齐。


具体是怎么样的对齐方式呢?

我们先来看以下代码:

struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));

大小不是1+4+1=6个字节哦。我们先看结果:

结果是12.是不是很不符合常理呢.

接下俩废话不多说,直接上内存对齐规则,然后我再一一解析它:


1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到
某个数字(对齐数)的整数倍的地址处。
 
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
   VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。


我们先一句话一句话来分析.

一.第一个成员在与结构体变量偏移量为0的地址处。

  偏移量可以抽象的理解为在内存中的位置。后面在讲解中慢慢会理解的.非常容易

例如上面那个例子:

第一个成员是 char c1;

那么位于偏移量为0的地址处,这样画一下会更容易理解.

 其中 0 - 16数字都是偏移量,数字2代表偏移量为2,16则代表偏移量为16.

  那么按照第一条规则:第一个成员应该在偏移量为0的位置,我们放入其中:

就成了上图所示.看接下来一条规则:

2. 其他成员变量要对齐到某个数字(对齐数)的最小整数倍的地址处。
 
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
   VS中默认的值为8

要对齐到对齐数的整数倍。首先看对齐数是编译器默认的和该结构体成员的较小值.

 vs编译默认的是8,而我们下一个类型即第二个类型为int类型,大小为4个字节.

所以我们取较小的值,即4,所以对齐数是4.

要对其到4的整数倍,我们此时只能取4的1倍了,如果是0,则4的0倍也是0,而偏移量为0的地方已经被占用,所以只能取4的1倍,即在偏移量为4的地方,开始占用一个int字节大小,空出来的空间,如偏移量为1,2,3的地方被浪费掉,不用再理.如下图:

 

接下来,第三个成员是char c2 : 

同样的办法,

vs编译默认的是8,而我们这个类型即char类型,大小为1个字节.

所以我们取较小的值,即1,所以对齐数是1.

然后对齐到1的最小倍数,既然是1的倍数,那肯定是多少都可以了,1,2,3,4都可,但是我们偏移量此时已经到了7的位置,所以下一个只能占用偏移量为8的位置,即8的倍数.

如下:

 此时所有成员已经对其完毕,接着看第三条规则:

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

此时的最大对齐数为结构体成员中占用空间最大的。

结构体成员类型分别为:char、int、char

最大的类型是int,即4个字节,即最大对齐数为4个字节..

 所以结构体的总大小为4的整数倍,注意此时已经占用了9个字节,不是8个字节!

因为偏移量包括0,所以此时只是偏移量为8,但已经占用了9个字节。

所以此时只能是4的3倍数,即12个字节。若是4的2的倍数,则是8,小于我们已经占用的字节了,所以8肯定不行。

所以综上,此结构体的大小为12个字节.

这样的懂了,那下面这样的代码该如何处置呢?

struct S4
{
char c1;
struct S1 s1;
double d;
};
printf("%d\n", sizeof(struct S4));

注:s1是上面例子的是s1

我们在S4结构体里嵌套了一个S1结构体变量s1,那这样我们该如何计算呢?

首先按照第一条规则,将c1放在偏移量为0的位置.

下面重点来了,下一个成员是一个结构体的变量,此时它便不再遵循第2条规则,直接看第4条规则: 

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

正常来说,我们将此成员的大小和编译器默认的大小作比较取最小值,然后找它的最小整倍数对齐。但是如果成员是一个结构体,它便不在按照自身的大小来计算,而是按照他自己的最大对齐数. 我们上面已经知道S1的最大对齐数是4,所以是4的倍数,此时只能从4的位置开始,即4的1倍数,如下:

 

接下来第三个成员是double类型,大小为8个字节,和编译器默认相同。那就取8,此时只能取8的2倍,即偏移量为16的位置.如下:

接着总大小为所有成员(包括嵌套的那个结构体)的最大对齐数的整数倍

他们的对齐数分别为1 4 8

所以最大对齐数是8,然后整数倍,只能是24,即8的3倍. 

即此结构体的大小为24.我们看结果:

结果正确.

这里还有几道题,希望有时间可以做一下来巩固一下自己所学的知识。 

 

//求S2结构体的大小
struct S2
{
char c1;
char c2;
int i;
};

//求S3结构体的大小
struct S3
{
double d;
char c;
int i;
};

结果自己可以运行尝试一下哦,看看与自己运算的是否相符。

如有疑问的地方,欢迎私信联系哦.

题外话:

为什么存在内存对齐?
大部分的参考资料都是如是说的:
1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常

2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访

结构体的内存对齐是拿空间来换取时间的做法.

---来源于比特就业课教材

那么结构体的内存对齐就到此为止了,还是一样,若是有疑问的地方,欢迎留言或者私信哦。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

次元工程师!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值