引例
先看这样一段程序。
// tStructSize.c
#include<stdio.h>
struct perInfo1{
int num; // 4 bytes
char name[10]; // 10 bytes
double account; // 8 bytes
};
struct perInfo2{
char name[10]; // 10 bytes
double account; // 8 bytes
int num; // 4 bytes
};
int main()
{
struct perInfo1 Tony1 = {1,"Tony",3.14159};
int structSize1 = sizeof(struct perInfo1);
struct perInfo2 Tony2 = {"Tony",3.14159,1};
int structSize2 = sizeof(struct perInfo2);
printf("Size : { int, char[10], double } = %d\n",structSize1);
printf("Size : { char[10], double, int } = %d\n",structSize2);
return 0;
}
我们用sizeof函数查看一下结构体的大小。拍脑瓜一想 4+10+8=22,然而编译执行后输出却并非如此。那为什么是24和32呢?定义变量顺序的不同还会影响结构体的内存空间大小吗?
结构体的内存空间分配
先给出一个类似公式的结构体大小计算方法:
结构体大小 = 末尾成员偏移量+末尾成员大小+末尾填充字节数
然后我们解释以下perInfo1的分配内存过程,以便理解上面的“公式”,当我们定义一个这样的结构体时,操作系统会为该结构体分配一段连续的存储单元。
struct perInfo1{
int num; // 4 bytes
char name[10]; // 10 bytes
double account; // 8 bytes
};
struct perInfo1 Tony1 = {1,"Tony",3.14159};
- 首先为成员变量num分配 4 个字节的内存,它是该结构体的第一个成员变量,所以num的地址就是perInfo结构体的首地址,无偏移。如图(假设这是内存中的地址空间);
Tips 偏移量:这里可以简单理解为结构体内成员变量的地址距离结构体首地址的相地指数的大小。
- 然后开始考虑为name分配空间,name是由10个char型组成,可以认为是连续定义10个char变量。由于结构体在做字节对齐的时候有这样一个规则:每个成员相对于结构体首地址的偏移量必须是当前成员所占字节数的整数倍,如果不是,操作系统就会自动在前一个成员变量后自动填充补齐。
当前偏移量是 4 ,是 1 (char类型)变量的的整数倍,不用填充,由如图分配。 - 为account分配空间:double型变量占用 8 个字节,而当前可分配地址的偏移量为 14 ,不是 8 的整数倍,需要做字节填充。
填充后我们把偏移量为16的地址空间分配给account成员变量。这样就完成了结构体 perInfo1 的内存空间分配(perInfo2还存在另一个问题),大小为 24 字节。 - 在结构体定义完成前,还会进行如下判断:
当前结构体的总大小是否为其最宽成员所占内存大小的整数倍,如果不是的话还要再将其填充成为整数倍。这也就是perInfo2的大小是 32 而不是 30 的原因了。
Tips perInfo2中当前结构体占位 30 字节,其最宽成员为 double 类型的 account 成员,占有 8 字节,因此要填充成 8 的整数倍 32 。
20210429更正图片如下。
- 我们也可以通过查看存储变量的地址来验证上面所述。
printf("Size : {int,char[10],double} = %d\n",structSize1);
printf("-----{%d, %d, %d }\n",&(Tony1.num),&(Tony1.name),&(Tony1.account));
printf("Size : {char[10],double,int} = %d\n",structSize2);
printf("-----{%d, %d, %d }\n",&(Tony2.name),&(Tony2.account),&(Tony2.num));
.
.
.
.
.
.
桃花仙人种桃树,又摘桃花换酒钱_