内存对齐
理论上,处理器可以访问处理任意地址的字节,但为了保证计算机读取的高效性,CPU会通过地址总线来访问内存。例如32位平台有32根地址线,即四个字节,所以每次就从内存读取4个字节。
为提高存储效率,编译器就会自动将一个数据尽量存放在一个步长之内,避免跨步长存储。这就是内存对齐。
结构体的存储
struct S
{
char ch;
int a;
double d;
};
对于这个结构体,是从内存的低地址开始开辟空间的,首先是char类型,占一个字节,即从机构提变量起始位置开始的第一个字节是存放ch的(即偏移量为0的地方开始向后一个字节);然后是int,它是4个字节,vs默认对齐数是8,所以a要对齐到偏移量(偏移量就是从结构体变量所在内存中的起始地址开始向后数几个字节)为4的整数倍的地方。已知第一个字节已被占用,而偏移量为4 的地方没有变量,所以a存储在从偏移量为4开始向后4 个字节内(即第五个字节到第8个字节。第八个字节对应的偏移量为7)。最后是double,它要对齐到偏移量为8的整数倍的地方,即8或16 或24……而恰好从偏移量为8的地方开始就没有变量了,所以d存放在从偏移量为8的地方起向后8个字节(即第9个字节到第16个字节)。到此,已经开辟了16个字节的空间了,那么此结构体变量所占空间是否为16呢?答案是肯定的。
因为结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。double d 的对其书最大,是8,所以此结构体只能开辟8或16 或24……字节的大小。
struct card
{
char ch;
double d;
int a;
};
对于这个结构体,按上述方法,ch从偏移量为0开始,而d只能从偏移量为8开始,这时已经开辟了16个字节的空间了,然后是a,它从4的倍数开始,即偏移量为16的地方,向后4个字节,这时已经20个字节了。那此变量是占20个字节吗?不是,而应该是8的倍数,即24.所以这种创建方式不如上一种创建方式好。
#pragma pack()改变默认对齐数
#pragma pack(2)是将编译器默认的对齐数改为了2,而不是原来的8.
#pragma pack()则是恢复默认对齐数
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#pragma pack(1)
struct stu
{
int a;
char ch;
double d;
};
struct card
{
char ch;
int a;
double d;
};
int main()
{
printf("%d", sizeof(struct stu));
printf("%d", sizeof(struct card));
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#pragma pack(1)
struct stu
{
int a;
char ch;
double d;
};
#pragma pack()
struct card
{
int a;
char ch;
double d;
};
int main()
{
printf("%d", sizeof(struct stu));
printf("%d", sizeof(struct card));
return 0;
}
第一个代码结果为13 13
第二个代码结果为13 16
这就是#pragma pack()的用处