引言
先看两个例子:
case1:
case2:
可以看到再计算结构体大小的时候,应为变量的位置不同,而导致得到的结构体大小不同,这是什么原因呢?本文将带你揭秘。
基本类型的大小
先来了解一下基本类型的大小吧
byte 1个字节
chart 2个字节
short 2个字节
int 4个字节
float 4个字节
long 8个字节
double 8个字节
三个原则:
首先我们需要了解三个原则,他是求解结构体大小问题的核心
1.结构体变量的首地址,必须是结构体 "最宽基本类型成员" 大小的整数倍(0被认为是任何数的整数倍)。
2.结构体每个成员相对于结构体首地址的偏移量,都是该成员的整数倍。
3.结构体的总大小,为结构体 “最宽基本类型成员” (将嵌套结构体里的基本类型也算上,得出的最宽基本类型) 大小的整数倍。//这三个原则都是为了服务一个约定 -- 内存对齐
什么是内存对齐
元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每个元素放置到内存中时,它都会认为内存是按照自己的大小(通常它为4或8)来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始,这就是所谓的内存对齐。
// 注: 内存对齐也是可以人为干预的,我们将在文章最后给出解答
为什么要内存对齐:
- 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
- 硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。
- 性能原因:数据结构(尤其栈)应该尽可能地在自然边界上对齐。
实例解答
回归主题,我们了解完三个原则后,内存对齐后,我们可以做下面的例子来进一步加深对结构体大小的理解
揭秘引言case
case1:
struct test
{
char c1;
char c2;
int n;
};
case2:
struct test
{
char c1;
char c2;
int n;
};
"最宽基本类型成员" 大小 是 int - 4 ,且结构体的大小是他的倍数
case1中c1 和 c2 只占一位,我们得到的最小大小是4,so后面来个空位就需要空出来,直到第五个格子,即下标是4的位置开始存入int 类型的 n
case2 中c1 后直接跟 n了,我们的n只能从下标4的位置开始存入,然后才再存c2,这样看的话,是不是觉得大小是4+4+1=9了? 大NO特NO,我们的三大原则第三条 -- 结构体的总大小,为结构体 “最宽基本类型成员” 大小的整数倍。
如果包含数组呢?
包含数组的结构体
核心 -- 数组是连续的内存空间,把数组看成连续存放的类型即可
struct test
{
char arr[5];
char c1;
int n;
};
你觉得是多少?
“最宽基本类型成员” 大小,是5吗?
不,是int -- 4
arr 和 c1 占了前面的8位,加上int的4位--大小就是 8+4=12
嵌套结构体的结构体呢?
嵌套结构体的结构体
struct test
{
char c1;
int n;
char c2;
struct testSon{
char c3;
int n2;
};
};
为什么是20呢?
其实这里只需要额外注意一点:外层结构体的c2 不和 内层的 c3共享4个连续内存空间
20 = 4(c1) + 4(n)+4(c2)+4(c3)+4(n2)
有的小伙伴已经忍不住要问,那联合体呢?
包含联合体的结构体:
核心 -- 联合体大小就是他的最大类型大小
struct test
{
char c1;
int n;
char c2;
union sizeof_Struct
{
char c3;
int n2;
double d;
};
};
还是先找最大类型大小 -- double -- 8
则: 24=8(c1 + n)+8(c2)+8(联合体)
那么下面我们玩点骚的咯 -- 指定对齐值
指定对齐值
直接行例子:
16 = 4(c1) + 4(n) + (4+4)(c2) , 最大类型是double -8 >4 对齐值是4
24= 8 (c1)+ 8(n)+ 8(c2) 我们这里的最大类型大小 8<16,对齐值取8
注: 由于硬件需求,我们的指定对齐值一般要求是4的倍数
总结:
对齐值取的是 min(指定值,最宽基本类型成员" 大小);