观察下面两个结构体,S1,S2中包含的结构体成员变量是完全相同的,只有顺序是不同的,但是如果我们执行程序,却会得出不一样的结果,用sizeof()去计算结构体的大小,我们得到结论,S1的大小是12字节,S2的大小是8字节,为什么会造成这样的现象,这就是本篇我们要介绍的知识点,结构体内存对齐。
#include <stdio.h>
#include <stddef.h>
struct S1
{
char c1;//
int i;//
char c2;//
};
struct S2
{
char c1;//1
char c2;//1
int i;//4
};
int main()
{
printf("S1=%d S2=%d", sizeof(struct S1), sizeof(struct S2));
return 0;
}
一.结构体对齐的规则
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
上述规则具体是什么意思我们来举例子说明。
1. 第一个成员在与结构体变量偏移量为0的地址处。
我们拿S1举例子,S1中第一个结构体成员是char类型的c1,依据规则,它从结构体零偏移的位置开始存储。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
首先我们来确定一个概念,什么是对齐数,你可以理解为元素的大小和编译器默认的对齐数的大小的较小值,我们依旧拿S1举例子,比如第一个变量c1是一个char类型的字符变量,它的大小是一个字节,所以它的对齐数大小就是从1和VS默认的8个字节调较小的那个,就是一个字节。同理,S1的第二个结构体成员i是一个int类型的变量,int类型的大小为4个字节,所以i的对齐数是4和8中较小的那个就是4。
我们知道了对齐数,我们回头去看第二个规则,我们知道i的对齐数为4个字节,所以,i应该存放在4的整数倍偏移量为起始位置的地方。
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
从上述的图中和程序的执行过程看,我们知道S1这个结构体的大小为12个字节,这个是由第三条规则引起的, 结构体的总大小是这个结构体所有成员变量的对齐数的最大值的整数倍,我们知道S1所有成员变量里对齐数最大为4,所以这个大小一定是4的整数倍,然后我们的c2存放在了偏移量为9的位置,所以我们这个结构体总大小得大于等于9且为4的倍数,所以为12。
知道了以上三条结论我们可以由此算出S2的大小。详细图解如下,如果没有算出来请好好再看一遍前三条规则的解释。
下面我们来验证这个事实
我们借助offsetof()这个函数来验证我们的猜想,这个函数可以返回给我们结构体成员在结构体中的偏移量。我们通过以下代码来验证我们上述讲解的内容。
offsetof()函数需要引用头文件stddef.h
#include <stdio.h>
#include <stddef.h>
struct S1
{
char c1;//
int i;//
char c2;//
};
struct S2
{
char c1;//对齐数从1/8中挑最小值,对齐数为1
char c2;//对齐数从1/8中挑最小值,对齐数为1
int i;//对齐数从4/8中调最小值,对齐数为4
};
int main()
{
printf("c1:%d\n", offsetof(struct S1, c1));
printf("i:%d\n", offsetof(struct S1, i));
printf("c2:%d\n", offsetof(struct S1, c2));
printf("c1:%d\n", offsetof(struct S2, c1));
printf("c2:%d\n", offsetof(struct S2, c2));
printf("i:%d\n", offsetof(struct S2, i));
return 0;
}
代码执行结果符合上述讲解。
我们探讨完了前三个规则, 那么第四个规则适用于什么情况呢?
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
我们来看一个例子,当在结构体中嵌套结构体。
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
int main()
{
printf("%d\n", sizeof(struct S4));
printf("%d\n", sizeof(struct S3));
printf("c1:%d\n", offsetof(struct S4, c1));
printf("s3:%d\n", offsetof(struct S4, s3));
printf("d:%d\n", offsetof(struct S4, d));
return 0;
}
第四条规则告诉我们,嵌套的结构体对齐到嵌套结构体最大的对齐数,虽然S3的大小是16个字节,但是对齐的时候,按照S3中最大的对齐数即8来对齐地址,所以s3直接对齐到8的地址处
以上就是我们结构体对齐的所有规则,如有疑问,请在下方留言或者发私信给我,希望能帮助大家答疑解惑!共同进步。