引言
在求结构体的大小时,绝大部分情况下不会直接等于各个成员大小的总和,编译器为了优化对结构体成员的访问总会在结构体中插入一些空白字节(内存对齐)例如:
struct S1
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(S1));
struct S3
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(S3));
结构体s1和s3的大小并不是我们想当然的各成员大小之和6,而是s1为8,s3为12之所以会出现这样的情况,是因为结构体的内存对齐
一 、为什么存在结构体的内存对齐
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的:某些硬件平台只能在某些地址处理某些特定类型的数据,否则抛出硬件异常。
2、数据结构应该尽可能的在自然边界上对齐,因为为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存仅需要访问一次,可以提高效率。
二、什么是内存对齐
通过牺牲空间来换取时间的方案就是内存对齐
三、内存对齐的原则
1、第一个成员不需要内存对齐(第一个成员在于结构体变量偏移量为0的地质处)。
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐:起始偏移量能整除对齐数
对齐数:编译器没有默认对齐数,一般为自身大小(即自身类型的大小,如char 为1 ,int 为4)。
3、结构体的大小为最大对齐数的整数倍,即最终结构体的大小一定能被最大对齐数整除(第一个成员也要参与计算)。
4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
含嵌套结构体的对齐数:就是结构体内的最大对齐数
下面我们来练习一下
首先我们来分析一下前面的s1,s3
struct S1
{
char c1; // +1
//第一个成员偏移量为0,只加自身大小1
char c2; // +1
// 因为1 可以被1(对齐数)整除,所以不用偏移,只加大小1
int i; // +2 +4
// 因为2(1+1)不能被4(对齐数)整除,所以需要+2(即加2个
空白字节表示偏移量)后才可以被4整除,再加自身大小4
8(1+1+2+4)可以被最大对齐数4整除所以s1的大小为:
1+1+2+4=8
};
printf("%d\n", sizeof(S1));
struct S3
{
char c1; // +1
//第一个成员偏移量为0,只加大小1
int i;// + 3 + 4
//因为1不能被4(对齐数)整除,所以需要+3(即加3个
空白字节表示偏移量)后才可以被4整除,在加自身大小4
char c2;// +1
//因为8(1+3+4)可以被1(对齐数)整除,所以不需要偏移,只加自
身大小1,
9(1+3+4+1)不能被最大对齐数4整除,所以还需要偏移三个字节,
即再+3 为12 可以被4整除,所以s3的大小为12
};
printf("%d\n", sizeof(S3));
再来一个小练习
struct S4
{
char c1; //1(自身大小)
short b; // +1 + 2(1为偏移量,2为自身大小)
double d; // +4 +8 (4为偏移量,8为自身大小)
//1+1+2+4+8=16,能被最大对齐数8整除,所以S4的大小为16
};
希望对您有所帮助!