引例
对于一个类型的数据它的大小我们已经有所了解,比如如下代码:
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
我们猜测它的代码运行结果是
4 4 8 2 4 8
运行结果为
那么对于一个结构体的大小,又该怎样去计算它的大小呢?
首先给出它的这个结构体的有关代码如下:
struct Stu
{
char name;
int age;
};
struct Stu A = { .name = 'k', .age = 20};
printf("%d\n", sizeof(A));
预想他的结果是1+4=5;
但是代码结果如下:
会发现与预想的结果截然不同,那么这边就有必要介绍一下结构体的内存对齐规则了。
结构体内存对齐的规则是为了使得结构体在内存中的布局更加紧凑和高效。具体的规则如下:
规则一
结构体的起始地址必须是它里面最大成员大小的整数倍,也就是说结构体的大小必须是最大成员大小的整数倍。
如上述结构体的大小为int大小的整数倍。
规则二
每个成员相对于起始地址的偏移量必须是它自身大小的整数倍,如果不是,则需要在前面填充一些字节,使得它满足这个要求。
关于引例代码的图示:
这里可以看到,第一个变量的第一个字节是从内存中的0位置处开始排布的。
规则三
从第二个成员开始,以后的每个成员都要对齐到某个对齐数的整数倍处
,这个对齐数必须是最大成员大小的整数倍处。
可以从上图中看到,当char占用了第一个字节后,int并没有直接跟在它的后面而是浪费三个字节的空间后,再开始跟在他后面。
规则四
整个结构体的大小,必须是最大对齐数的整数倍,最大对齐数包含中嵌套的结构体成员中的对齐数。
如下列代码,当在结构体嵌套了另一个结构体时:
struct Stu
{
char name;
int age;
};
struct Book
{
struct Stu B;
char name;
short price;
};
struct Book K = { .B.age = 18,.B.name = 'z',.name = 'm',.price = 12 };
printf("%d\n", sizeof(K));
那么此时这个struct Book k结构体的大小的最大对齐数就变为了4,而不是2。
那么这个结构体的大小也就能算出来是12.
那么为什么会有结构体内存对齐这么一个说法呢?
参考文献是这样说的:
1.平台原因(性能原因)
不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
2.性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,==为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问==。
所以总体上来说,结构体的内存对齐是拿空间来换取时间的做法。
修改默认对齐数
VS环境下默认对齐数是8。
gcc环境下没有默认对齐数,没有默认对齐数时,对齐数就是成员自身的大小
那么如何修改就要用到 #pragma这个预处理指令
#pragma pack(8) //修改默认对齐数为8
#pragma pack() //取消修改的默认对齐数,还原为默认值
注:当结构体的默认对齐数不适用时,就可以修改默认对齐数。
多谢观看,有问题欢迎留言~~