目录
结构体内存对齐规则(重要)
结构体里面存有变量,当然也就占用内存。这个内存不是说有什么变量有多少变量加起来就行而是有一定规则。
对齐数 = 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值。
注:VS中的默认对齐数为8,不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数
内存计算对齐规则:
1.成员排序放在结构体的内存地址里。假设结构体在0位置,第一个成员变量也就从0开始。
2.每个成员开辟的空间都是对齐数的整数倍。
2怎么理解,对齐数每个变量都有一个,2就是说给变量空间时,根据对齐数的倍数来存。就比如int 的 对齐数为4,那他只能在结构体的 0 4 8 12的位置存。
3.最终结构体的大小为最大对齐数的整数倍。
4.如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。(把结构体也当做一个变量)
结构体大小计算3步(重要)
有了对齐数的概念,我们就可以做结构体的计算了。
struct S
{
double d;
char c;
int i;
};
第一步找对齐数: (默认对齐数和当前变量的大小取较小值)
第二步构思位置:(根据对齐数将成员变量放入内存中)
第三步找到最大对齐数:(结构体的最终大小根据最大对齐数来定)
如图我们可以看到目前用到的内存是0-15,其中9 - 11浪费了(变量找位置根据对齐数找);
0 - 15为16字节,然后找到我们最大的对齐数,这里是double,为8,此时为16字节为8的倍数,所以要开辟的结构体大小就是16字节。
内存对齐有什么用
平台原因:
有两种访问内存数据的方法:1.任意访问任意位置的数据2.根据特殊类型的大小按倍数寻找。
因为不是每个平台都是1的访问方法,所以为了兼容有了内存对齐。
性能原因:
对齐后提高了访问效率:
减少总线传输次数:处理器访问内存时,对齐的数据可以在较少的内存访问周期内被读取或写入,因为它们往往与处理器的数据总线宽度对齐,从而减少了总线传输次数。
空间换时间:
因为对齐内存不免会产生一些没有用到的内存,但是效率提升。
构造结构体技巧
上面说到结构体内的变量是按顺序在内存中存储的。那更换变量的位置或许能减少空间的损耗呢?这个想法是对的
struct S1
{
char a;
char b;
int c;
};//结构体1
struct S2
{
char a;
int c;
char b;
};//结构体2
上面两个结构体,里面的变量交换了位置,但是S1计算得只消耗了8字节 ,但是S2消耗了12字节。两个的最大对齐数都是4,但是int在中间导致b存储完是在9字节的位置。所以为了是最大对齐数的倍数又额外开了3字节到了12字节
所以结论就是:把较小对齐数的变量放到一起
修改默认对齐数
#pragma pack()
这个预处理指令中()里填的就是对齐数
#include <stdio.h>
#pragma pack(4)//设置默认对齐数为4
struct S1
{
char a;//1/4->1
int b;//4/4->4
char c;//1/4->1
};//12
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char a;//1/1->1
int b;//4/1->1
char c;//1/1->1
};//6
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
printf("%d\n", sizeof(struct S1));//打印结果为12
printf("%d\n", sizeof(struct S2));//打印结果为6
return 0;
}
代码中第一次的默认对齐数为4,第二次的为1.最终计算结果也是不一样的。