内存对齐由编译器负责,编译器为程序中的每个"数据单元"安排在适当的位置上。
内存对齐原因:
(1)平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
(2)性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
内存对齐规则:
每个特定平台上的编译器都有自己的默认"对齐系数"(也叫对齐模数)。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值的大小将不产生任何效果。
内存的自然对齐:每一种数据类型都必须放在地址中的整数倍上
例如:
地址4可以放char(1)类型,可以放int(4)型,可以放short(2)型,但是不能存放double(8)型,仅仅因为4不是8的整数倍。
地址3能存放char型,但是其他int,short,double都不能存放。
有一个特殊地址,就是0,它可以是任何类型的整数倍,所以可以存放任何数据。
结构体内存分配原则:
1.结构体的总大小,必须要是其内部最大成员的整数倍。
2..结构体或union联合的数据成员,第一个数据成员是要放在offset == 0的地方,如果遇上子成员,要根据子成员的类型存放在对应的整数倍的地址上
3..如果结构体作为成员,则要找到这个结构体中的最大元素,然后从这个最大成员的整数倍地址开始存储(strutc a中有一个struct b,b里面有char,int,double….那b应该从8的整数倍开始存储)
4. 结构体或类的静态成员变量不计算在内存对齐中, sizeof只计算栈内分配内存
例如
class Base{ int a; static double b; char c; }; double Base::b = 0; cout << sizeof(Base); // 值为8