内存对齐
1、内存对齐是什么?
计算机的内存就好像一个很大的数组,访问内存的时候,看似是可以从任何地址开始,但是实际上为了简化形成处理器和内存系统之间的接口设计,不同类型的数据会按照一定的规则在空间上排列,并不是按照顺序一个接着一个排放,这种排放方式就是内存对齐。
2、需要内存对齐的原因
内存对齐能够提升处理器读取数据的效率,比如,假设一个处理器总是从内存中读取8个字节或者8个字节的倍数,如果储存double类型数据的地址不是8的倍数,那么可能需要读取两块8个字节的内存才能读取到这个double类型数据,若double类型数据的地址是8的倍数,则读取一次内存就能读取到这个double类型数据。
3、如何实现内存对齐
在写程序的时候,优秀的编译器将会自动实现对齐的策略,一般的编译器比如VS、DEV C++等,默认都是8位字节对齐,就算不实现自动对齐,编译器也不会报错,只是会在效率上受影响,内存对齐可以看成一种使用空间效率换时间效率的方法。
对齐的原则:任何K字节的基本对象的地址都必须是K的倍数,其基本类型的对齐K参数如下表所示:
K | 类型 |
---|---|
1 | char |
2 | short |
4 | int,float |
8 | long,double,char* |
每种类型都按照上表的K来完成内存对齐。编译器在汇编代码中放入命令,来指令全局数据所需的对齐,例如,若要保证数据的起始地址是8的倍数,则可以包含以下命令:
.align 8
在C语言中设置对齐的方式是,在开头声明如下代码:
#pragma pack(value)
//value为要设置的字节对齐数
对于包含结构的代码,编译器会在字段的分配中插入间隙,从而保证每个结构元素都满足它的对齐要求,例如下面这个结构,假设编译器设置为4字节对齐:
struct a
{
int i; //4个字节
char c; //1个字节
int j; //4个字节
};
int main()
{
cout<<sizeof(a);
}
理论上来说,这个结构体由两个int和一个char组成,其内存大小为9字节,然而输出为:
理论上的内存分布,如图所示:
j的地址偏移为5,无法满足4字节的对齐要求,因此编译器会在字段c和j之间插入一个3字节的间隙:
插入间隙之后,j的偏移量为8,便满足4字节的对齐要求,因此整个结构的大小也变为12字节。除了考虑结构体内的元素地址外,结构体的起始位置也就是图中的"0",不一定是真实内存中的0,可能是任意一段内存的起点,所以其结构体的起点,也就是struct a*类型的指针必须也满足4字节对齐,这样也就保证了结构体内的元素在整体内存布局下也是满足4字节对齐的。