内存对齐
为什么需要内存对齐?
- CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是1,2 ,4 ,8 ,16…字节
- 当读取操作的数据末尾对齐,则需要两次总线周期来访问内存,因此性能会大打折扣
- 某些硬件平台只能从规定的相对地址读取特定类型的数据,否则产生硬件异常
什么是内存对齐?
- 不同类型数据在内存中按照一定的规则排列
- 不一定是顺序的一个接一个的排列
struct A
{
char a;
short b;
char c;
int i;
};
struct B
{
char a;
char b;
short c;
int i;
};
那么上面结构体A和B所占的内存空间是否相同? 就让我们通过实验得知吧! 如下:
#include <stdio.h>
struct A
{
char a;
short b;
char c;
int i;
};
struct B
{
char a;
char b;
short c;
int i;
};
int main()
{
printf("sizeof(struct A) = %d\n", sizeof(struct A));
printf("sizeof(struct B) = %d\n", sizeof(struct B));
return 0;
}
实验结果如下:
显而易见A和B所占的内存空间并不相同这就是由于CPU读取内存时不连续造成的,那么我们在此将引入一个特殊的指令 : #pragma pack()
- .#pragma pack() 用于指定内存对齐方式,能够改变编译器的默认对齐方式
用法如下:
#include <stdio.h>
#pragma pack(2) //值得注意的是编译器默认情况下按照四字节对齐方式
struct A
{
char a;
short b;
char c;
int i;
};
struct B
{
char a;
char b;
short c;
int i;
};
#pragma pack()
int main()
{
printf("sizeof(struct A) = %d\n", sizeof(struct A));
printf("sizeof(struct B) = %d\n", sizeof(struct B));
return 0;
}
实验结果如下:
根据运行结果发现这个时候的结果与最初的有所不同这就是我们利用#pragma pack()指令改变了编译器的默认对齐方式造成的。
struct占用的内存大小计算
- 第一个成员起始于0偏移处
- 每个成员按其类型大小和pack参数中较小的一个进行对齐
- 偏移地址必须能被对齐参数整除
- 结构体成员大小取其内部长度最大的数据成员作为其大小
- 结构体总长度必须为所有对齐参数的整数倍
那么根据上面的学习我们来做个示例检验一下我们是否掌握了这些知识
示例代码如下(微软面试题):
#include <stdio.h>
#pragma pack(8)
struct S1
{
short a;
long b;
};
struct S2
{
char c;
struct S1 d;
double e;
};
#pragma pack()
int main()
{
printf("sizeof(struct S1) = %d\n", sizeof(struct S1));
printf("sizeof(struct S2) = %d\n", sizeof(struct S2));
return 0;
}
实验结果如下:
大家的结果是否和我的一样呢?我在下面附上代码分析:
#include <stdio.h>
#pragma pack(8)
struct S1
{ // 对齐参数 偏移地址 大小
short a; // 2 0 2
long b; // 4 4 4
};
struct S2
{ // 对齐参数 偏移地址 大小
char c; // 1 0 1
struct S1 d; // 4 4 8
double e; // 8 16 8
};
#pragma pack()
int main()
{
printf("sizeof(struct S1) = %d\n", sizeof(struct S1));
printf("sizeof(struct S2) = %d\n", sizeof(struct S2));
return 0;
}