内存对齐


内存对齐是编译器职责。编译期间,编译器按照对齐规则计算数据单元存储位置,以便把数据单元安排到“恰当”的位置;

内存对齐的时机

编译器编译器期间为数据单元分配内存时。但严谨的讲,这里的“分配”应该理解为“计算”,因为真正的物理内存分配发生在程序运行时。

为什么要对齐

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

对于第二点,可以简单理解为对齐能让cpu用最少的访问次数读取数据。

对齐模数

介绍对齐规则之前,首先要知道“对齐模数”;每个特定平台都有自己默认的“对齐模数”,通常和cpu相关,比如32位系统模数为4,64位系统为8。除了默认模数,也可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一模数,其中的n就是你要指定的“对齐模数”。

对齐规则

1.成员对齐: 结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

2.整体对齐:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。·

3.结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

Win32平台对齐规则策略

win32平台默认对齐模数n为4;
结构体成员最长基本类型长度和n,最小者记为m;

1) 结构体变量的首地址是m的整数倍;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小和n其中最小者的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。

3) 结构体的总大小为结构体m的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding);

a. 结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
b. 如果结构体内存在长度大于处理器位数的元素,那么就以处理器的位数为对齐单位;否则,如果结构体内的元素的长度都小于处理器的位数的时候,便以结构体里面最长的数据元素为对齐单位。

4) 结构体内类型相同的连续元素将在连续的空间内,和数组一样;

本文非原创,内容整理以做备忘用。

知识共享许可协议本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,请务必在遵守许可协议的前提下转载。
发布时间:2019-02-27 15:36:13 阅读:315 标签:技术笔记