计算结构体的大小
计算结构体的大小并不是将变量所占的字节数相加,而是按照一定的对齐规则进行计算结构体的大小的。
优点:按照计算机的访问规则,这种对齐规则提升了效率。
缺点:浪费空间。
结构体大小的计算必须满足两条原则:
-
结构体每一个成员相对于结构体首地址的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)。
-
结构体的总大小必须是所有成员大小的整数倍(数组成员除外,结构体成员除外)。
一般的结构体
struct s1
{
char a; //第一个成员的偏移量都是0,成员大小是1
char b; //偏移量是1(前面的偏移量+成员大小), 成员大小是1
int c; //偏移量是1+1=2(前面的偏移量+成员大小), 成员大小是4
//偏移量(2)必须是当前结构体成员大小(4)的整数倍
//偏移量(2)不是当前结构体成员大小(4)的整数倍,所以当前偏移量(2)要+2
//1+1+2+4=8
};
int main()
{
printf("s1:%d\n",sizeof(struct s1));
return 0;
}
运行结果:
再来看看下来的这个结构体
struct s2
{
char a; //1
int c; //4
char b; //1
//1+3+4+1=9
//结构体的总大小必须是所有成员大小的整数倍
//但9不是所有成员大小的整数倍,所有要继续向后偏移,所以是12
};
运行结果:
计算这个结构体的大小如果只按结构体对齐原则的第一条,结构体大小就应该为9(9 = 1+4+3+1,3为补齐的字节数),但是结构体的大小为12,那是因为还要满足结构体对齐原则中的第二条,结构体总大小为所有成员大小的整数倍,(9不是4的整数倍),所以要继续向后偏移,直到达到所有成员的整数倍为止
成员包含数组的结构体
数组不满足结构体总大小为是所有成员大小的整数倍这个条件。
struct s3
{
char a; //1
int c; //4+1+3=8
char b[10]; //10+8+2=20
//10+8=18,18不是10的整数倍,所以要继续往后偏移2,18+2=20
};
运行结果:
这个char类型的数组,只需要把它看做十个char连在一起就可以了,加起来就是18,在满足结构体总大小为成员整数倍(18不是10的整数倍,要继续往后偏移2),所以大小就是20。
那为什么结构体的总大小必须是所有成员大小的整数倍时数组要除外呢?
struct s4
{
char a; //1
int c; //4+1+3=8
char b[12]; //12+8+4=24
//12+8=20,20不是12的整数倍,所以要继续往后偏移4,20+3=24
//但是s4的真实结构体大小是20,为什么呢?
//因为数组不满足结构体总大小为是所有成员大小的整数倍这个条件
};
运行结果:
可以看到当数组大小改为12时,结构体大小还是为20,并不是上面计算出的24,因为数组不满足结构体总大小为是所有成员大小的整数倍这个条件。
成员包含结构体的结构体
成员包含未被定义的结构体的结构体
如果只是声明结构体,那么算结构体大小时就会忽略结构体,结构体不占空间,要定义了结构体变量才占内存空间。
struct s5
{
char a; //1
int c; //4 +1+3=8
struct x{
char m;
int n;
};
//在这里x结构体,只是声明,没有被定义,可以忽略未被定义的结构体,未被定义的结构体结构体不占空间
float f; //4 +8=12
};
如果在linux环境中,若被包含的结构体只是声明,没有定义的话,编译会出现警告,不过问题不大。
运行结果:
成员包含已经定义的结构体的结构体
已经定义的结构体的结构体才算大小。
struct s6
{
char a; //1
int c; //4 +1+3=8
struct y{
char m; //1
int n; //4 +1+3=8
}tmp;
//结构体y已定义成tmp,该结构体占空间
//8 +8=16
float f; //4 +16=20
};
运行结果:
可以看到,如果只是声明结构体,那么算结构体大小时就会忽略结构体,结构体不占空间,要定义了结构体变量才占内存空间,还应该注意,在这里对于不同的编译器会出现不一样的结果,有的编译器在没有定义变量的时候结构体大小也是20,在定义了结构体大小的时候也是20,有的编译器在没有定义结构体变量时就会忽略结构体。
在这里,里面这个结构体的大小是8,那么结构体大小是否就要向8对齐呢?这个结构体的大小是20,很明显不是8的倍数,(所以原则二中要去掉结构体)。
成员包含联合体的结构体
联合体大小就是联合体中成员大小中最大的成员的大小
struct s7
{
char a; //1
int c; //4 +1+3=8
union z{
char m;
int n;
};
//4 +8=12
//联合体大小就是联合体中成员大小中最大的成员的大小,成员大小最大的成员是int为4
};
在联合体前面大小为8,因为联合体大小就是联合体中成员大小中最大的成员的大小,这个联合体最大成员大小为 int,4个字节,(8是4的整数倍)所以这个结构体大小是4+8=12。
运行结果:
指定对齐值 #pragma pack()
当指定的对齐值pragma pack()小于成员大小最大值时,按指定的对齐值计算 。
当指定的对齐值pragma pack()大于成员大小最大值时,按照最大成员来计算。
当指定的对齐值小于成员大小最大值时
#pragma pack(4) //指定向4对齐,成员大小最大值是8(double)时
struct s8
{
char a; //1
int c; //4 +1+3 =8
float f; //4 +8=12
//到这里是12按照4对齐的话12是4的倍数,所以就是12+8=20
double d; //8 +12+4=24
//如果按照8对齐的话12不是8的倍数,所以要再偏移4,最后大小等于24
//#pragma pack(4)是指定向4对齐,所以不用再偏移4,直接8+12=20
};
运行结果:
当指定的对齐值大于成员大小最大值时
#pragma pack(16)
//当指定的对齐值pragma pack()小于成员大小最大值时,按指定的对齐值计算
//当指定的对齐值pragma pack()大于成员大小最大值时,按照最大成员来计算
struct s9
{
char a; //1
int c; //4 +1+3 =8
float f; //4 +8=12
double d; //8 +12+4=24
};
运行结果:
Salute to 老陈!!!