自定义类型(1)结构体详解(内存对齐.)

本文是对结构体进阶知识的讲解,如果没有掌握结构体的基本知识,可以先看下面的博客
从0到1助你掌握结构体

在从零到一助你掌握结构体这篇文章中,介绍了关于结构体的初阶知识,并且留了一些悬念,结构体的内存到底是如何分配的?以及为什么这么分配?下面,就让我们深入了解一下:
在这里插入图片描述

结构体内存对齐:

让我们观察以下代码,并思考结构体占的内存是多少:

typedef struct student
{
	int age;
	char name[3];
}student;

按照我们正常的思想,把结构体内部的变量所占的字节大小加在一起 4+3 = 7
应该是占7个字节,但实际上是这样吗?让我们打印一下:
在这里插入图片描述
我们可以看到,结构体的大小居然是八,他到底为什么是八呢?我们该如何计算呢?
在了解之前,我们先看一看,这结构体的八个字节,到底是怎么分配的:
在这里插入图片描述

这里引出的一条规则就是:结构体的大小必须是其实际对齐数的整数倍
如果不是,则要补齐,所以本应该大小为7的结构体,实际大小为8。
这里提到了一个对齐数的概念
对齐数:每个编译器都有一个默认的对齐数,在vs中是8,每个变量的对齐数都是自身大小与默认对齐数的较小者
这个对齐数有什么作用呢?他决定了你在结构体中的位置,比如你是一个int变量,你的起始位置就不能是3,不能是5,不能是6,只能是4的整数倍
举个例子:

typedef struct student
{
	char a;
	char b;
	int c;
}student;

我们来计算一下这个结构体的大小,常理来说,a,b各占一个字节,c占四个字节,这是六个字节,再利用最后的补齐规则,补成4的整数倍,那就是八个字节
在这里插入图片描述
看起来确实是这样,但实际上在内存中的存储真的是这样吗?我们如何验证呢,这时候可以借助一个函数:offsetof函数,简单的看一下需要引用的头文件和参数类型:
在这里插入图片描述

这个函数是用于查看结构体变量在结构体中的偏移量的,偏移量是相当于结构体首地址的偏移程度,也可以理解成你在这片空间中起始地址是从第几块开始的。
在这里插入图片描述
在这里插入图片描述

方便理解,我们把内存画成图,如下:
在这里插入图片描述
这个图又两个要点:

结构体中的内存分布并不是我们想的那样,1,1,4,2,而是1,1,2,4,这是因为我们在给c空间的时候,他的对齐数是8和4中的较小值,就是4,所以他的起始地址必须是4的整数倍,所以他能从2的位置开始赋予内存,只能往下找,直到找到4,4的一倍,这样才能给他空间,所以是1,1,4,2。

对齐补的空间并不属于任何一个变量,这是如何体现的呢:
在这里插入图片描述

看这张内存图,我们给b一个256,整个内存的空间是:
01 00 cc cc 01 00 00 00 ,正常来说b的空间是 第一个空间,也就是01 后的空间,如果补齐的字节是属于b的,那我们给b一个256,他是能够存下的因为我现在有了三个字节,一个字节存储的是 8位,三个字节是24位,可实际上存储的数据是00,这就说明,后面的字节不属于b,而是一个独立的空间。

那么,我们再做一个小练习,若是结构体嵌套结构体呢?

typedef struct student
{
	char a;
	char b;
	int c;
}student;	
typedef struct std
{
	student a1;
	char b1;
	int c1;
}std;

std的大小是多少呢?其各个元素的偏移量又是多少?我们来分析一下:
std中的第一个成员变量是student,他的空间还是按照,之前的规则算,对齐数也是一样,那就是8个,b1,对齐数是1,所以偏移量是8,c1必须是4的整数倍,所以是12,那么整个结构体所占的字节数就应该是8+1+3+4 = 16
在这里插入图片描述

小结

在这里我们小结一下结构体对齐的规则
1)
第一个成员变量在与结构体变量偏移量为0的地址处(起始地址)
2)
其他成员变量要对齐到该变量对齐数的整数倍地址处
3)
成员变量的对齐数是编译器默认的对齐数与该成员大小的较小值
4)
结构体的总大小为最大对齐数的整数倍
5)
在嵌套的情况下,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍处

为什么要内存对齐?

大体上能分为两种原因 :
1.平台原因
因为不是所有的硬件平台都能访问任意地址的任意数据的(比如只能访问4的整数倍的地址的数据),否则会抛出硬件异常。
2.性能原因
举个例子,寄存器一次能取出4个数据,你如果没进行内存对齐,你可能取到的是int 的一部分和一个char,那么你想取一个int,就可能需要两次,但是经过了内存对齐以后,你一次肯定能取到你想要的数
也可以说,内存对齐就是牺牲了内存的空间来换取运行效率的一种做法

后记

这只是结构体提升效率的一种方法,而且是以空间换时间,在下一篇博客中将会介绍节省空间的方法—>位段
其实在举例子的时候有一些纰漏,为了证明为了对齐申请的空间与成员变量无关的那个例子,没有确定他到底是截断的还是确实表示了256,只不过是循环成0了,这个大家可以多举几个例子证明或者推翻我的观点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值