总目录
1.结构体内存对齐的规则
2.实例图文分析
3.为什么存在结构体内存对齐(+实例分析)
4.修改默认对齐数
5.结构体成员的排序
6.最后
1.结构体内存对齐的规则
1.第一个成员在结构体变量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认的对齐数与该成员大小的较小值
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍数,结构体的总大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
(vs的默认对齐数为8,linux没有默认对齐数)
2.实例图文分析
这里我们使用vs进行演示
例①
>由规则1 “第一个成员在结构体变量为0的地址处”,故c的位置在0地址处(橙色部分),又因为是char类型,只占第一个字节
>由规则2,该成员变量为int类型,大小为4个字节,比vs默认对齐数8要小,故i对齐到对齐数4的整数倍即下标为4的位置(绿色部分),并向下占用4个字节
>同理,d为double类型,大小与默认对齐数相同,故从i的结束位置向下找8的整数倍数,并占用8个字节
>蓝色部分即为结构体内存对齐遵循规则浪费的空间
执行程序的结果如下
>照应规则3: 由结果值为16,该结构体的最大对齐数为8,结构体总大小为16,为最大对齐数的整数倍
例②
>同例①一样,c1的位置从S2的起始位置0处开始
>由例①知,struct S1 s1的大小为16,大于默认对齐数,则对齐数为较小值8,故从对齐数的整数倍下标为8的地址处开始,并向下占用16个字节
>d1的大小是8个字节,与默认对齐数一致,故向下取8的整数倍,而24恰好为8的整数倍,则从24开始向下占用8个字节
>蓝色部分即为结构体内存对齐遵循规则浪费的空间
执行程序的结果
>照应规则4,例②嵌套了结构体(S1),且嵌套的结构体(S1)对齐到自身成员内最大对齐数8的位置,而结构体(S2)的总大小为32,恰好为S1的整数倍
3.为什么存在结构体对齐
大多参考资料说法:
1.平台原因(移植原因):并不是所有的硬件平台都能访问任意地址的任意数据,一些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。因为,当访问未对齐的内存时,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
书面语当然难以理解,举个例子
>上图为存在内存对齐,下图不存在内存对齐,假设系统一次读取4个字节
>当读取i的时候,上图先读取前四个块,再读取后四个块,一次可以读完i
>而下图第一次读前四个块,第二次读后四个块,将i分两次读完
可以看出内存对齐后的效率有所提升,由此可以得出结论
结构体的内存对齐是拿空间换取时间的做法
4.修改默认对齐数
不多说,看代码
#include <stddef.h>
#pragma pack(2)//修改默认对齐值
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//结束对默认对齐数的修改
int main()
{
printf("%d\n", sizeof(struct S));
return 0;
}
>上述代码块,在一定范围内将默认对齐数改为了2
5.结构体成员的排序
一个小建议,当使用结构体的时候可以适当的调整结构体成员的排序,因为不同的排序可能导致结构体的总大小有所差异,正确的排序可以占用更少的大小
简单举个例子
struct S1
{
char c1;
int i;
char c2;
}s1;
struct S2
{
char c1;
char c2;
int i;
}s2;
void main()
{
printf("%d\n", sizeof(s1));//12
printf("%d\n", sizeof(s2));//8
}
6.总结
如果能帮助到你的话不妨点个赞学习路上一路加油!