一、结构体
1.结构体大小的计算
结构体的存放规则:
1)结构体第一个成员在结构体变量偏移量为0的地址处。
2)其余成员的偏移量和对齐数的整数倍对齐,跳过的空间被浪费。
对齐数 = min(编译器默认对齐数,成员大小)。
vs默认对齐数为8,那么对于一个int成员,对齐数就是min(8,4)= 4 。
3)结构体总大小为成员中最大对齐数的整数倍(不足则扩大至满足条件)。
4)如果嵌套结构体,那么嵌套的结构体对齐到其成员最大对齐数的整数倍的偏移量地址,总结构体在计算总大小比较对齐数时,将嵌套的结构体内成员的对齐数也要考虑在内。
实例分析
2.修改默认对齐数
可以通过使用pragma这个预处理指令的方式来修改系统默认对齐数。
//设置默认对齐数为1
#pragma pack(1)
struct S1
{
char c1;
int a;
char c2;
}s1;
#pragma pack()
//取消设置的默认对齐数
int main()
{
printf("%d\n",sizeof(s1)); // 6
return 0;
}
3.计算偏移量
可以通过offsetof宏的方式来实现对成员偏移量的计算。
struct S1
{
char c1;
int a;
char c2;
};
int main()
{
printf("%d\n",offsetof(struct S1,c1)); // 0
printf("%d\n",offsetof(struct S1,a)); // 4
printf("%d\n",offsetof(struct S1,c2)); // 8
return 0;
}
二、位段
1.位段的定义
位段是结构体的一种特殊形式,位段的声明和结构体相似,但有两个不同:
1)位段的成员必须是int , unsigned int , signed int 或char类型。
2)位段的成员名后有一个冒号和一个数字。
struct A
{
int a:2;
int b:5;
int c:10;
int d:30;
}
2.位段的内存分配
1)声明成员时,冒号后的数字代表为该成员需要的的二进制位数,该数字不能大于使用机器的位数,因为要以机器的位数个比特位为一组分配空间,如果大于,则一次申请分配不够用,出现错误。
2)系统为成员分配空间时,一次给出4个字节(成员为int)或1个字节(成员为char)。
3)成员按顺序从分配的空间内取出自己需要大小的空间(二进制位)。
4)当本次分配的字节还足以下一个成员使用则继续,若不足以给下一个成员,则本次分配剩余空间浪费,新分配字节供给下一个成员使用。
5)最后整个位段的大小即为总共提供的字节数。
6)补充:可以在调试过程中提供观察内存判断位段分配空间是从右向左或者是相反。
三、联合(共用体)
1.联合的定义
联合是一种特殊的自定义类型,这种类型定义的变量也包含一系列成员,但特别的是这些成员共用一块空间。
2.联合的大小计算
1)联合的大小至少为最大成员的大小。
2)当最大成员大小不是最大对齐数的整数倍时,需要扩大至整数倍。
union Un
{
int a;
char arr[5];
};
int main()
{
union Un u;
printf("%d\n",sizeof(u)); //8
return 0;
}
很容易分析,成员最大的大小为5个字节,而5不是对齐数(4)的整数倍,所以需要扩大至整数倍,所以得到最后的大小为8字节。