关于结构体所占空间大小怎么计算呢
这时候我们引入一个名词“内存对齐”
先来看一组现象:
struct S
{
char name[20];
int score;
int age;
double weight;
};
#include<stdio.h>
int main()
{
printf("%d\n",sizeof(struct S));
return 0;
}
结果:
40
Program ended with exit code: 0
咦?不应该是20+4+4+8 = 36吗
其实在结构体里存在内存对齐现象:
我们知道,不同类型占用字节数不一样,比如int-4,char-1,short-2等等,那么在我们系统里还有一个数叫做默认对齐值,这个值干嘛用的?是用来和不同类型变量比较的。
比较干什么?
他们比较,较小的数的倍数来做这个特定类型的偏移量。
什么是偏移量?
我们定这个结构体最开始储存数据的偏移量为0,并依次向下顺延,那么char型数组占20个字节,就占用了偏移量为0~19的位置,下一个变量储存的时候,起始位置偏移量必须在20及其以后(一个偏移量其实对应一个字节)
比如说,VS环境的默认对齐值是8个字节,也就是说,我int型4比8小,那我int就一定存储在4的倍数的偏移量上,那么char型数组占完19偏移量以后,我int型数据score一定要放在19后面,且为4的偏移量的位置上,也就是20,以此类推,age数据放在24上,double数据要放在8(8与8相比的最小值)的倍数上,也就是从32开始放占用偏移量32~39(也就是8个字节,大小和double类型变量一样)
后面还有结构体大小运算的举例
最终计算结构体大小,不只是0~39 共40个字节这么简单,还要以这里面最大的数据单位(double-8)为基准,找他的倍数
比如我最后一个数据储存到偏移量指向35就截止了,但因为我结构体里面最大的数据类型是double-8,所以我整个结构体要以8的倍数结尾,也就是最终指向40,最终(这段的例子)结构体大小就是40个字节
所以我们在进行创建结构体的时候,要尽量减小大小,就要尽量把占用空间小的变量放在一起,比如:
struct S
{
char a;
char b;
int c;
double d;
}s1;
printf("%d\n",sizeof(s1));
结果为16;
因为a指向偏移量0,占用0~1字节
b指向偏移量1,占用1~2字节
c指向偏移量2,但c为int类型,不能从偏移量为2的地方开始,需要从4和默认对齐数(一般为8)的较小值的倍数开始,即向后找,找到偏移量为4的位置,占用4~7字节
d则是相同的道理,取8和8的较小值:8,从8的倍数(8)开始,占用8个字节,即占用8~15字节
最终结构体的大小还不是0~15 共16字节这么简单,还要比较这个数(16)是不是结构体成员里面最大的变量占的空间的整数倍,一比,嗯,16确实是最大的double的整数倍,所以结果是16
而如果占用空间小的变量不放在一起呢?
给出了这样一个例子:
struct D
{
char a;
int b;
char c;
double d;
}s2;
printf("%d\n",sizeof(s2));
结果为24,方法如上分析
大家可以发现,明明相同类型的结构体(里面成员类型相同),大小差了这么多,是不是感到了这样节省内存的必要性!
上面提到了默认对齐数,指编译环境的默认对齐数(ps.VS环境是8),那我们可以手动修改这个数吗?
答:是可以的
方法:
#pragma pack(8)
类似于define的使用方法,语句后面不用加分号,整条语句放在函数体外面,括号里的数字就是我们设置的默认对齐数,一般方便起见,设置为2,4,8这样的数
设置完怎么消除,回复成默认的?
只要在结构体定义完以后
#pragma pack()
就可以了
整体效果像这样:
#include<stdio.h>
#pragma pack(8)
struct S
{
char a;
int b;
double c;
};
#pragma pack()
int main()
{
struct S s1;
return 0;
}
不需要额外的头文件