C/C++语言内存对齐
内存对齐:在计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任何类型的变量。但实际上在访问特定类型的变量的时候需要从特定的地址开始,这就需要各种类型的数据按照一定的规则在空间上排列,而不是顺序的一个接一个的存放,这就是内存对齐,也叫字节对齐。
内存对齐的作用:
- 可移植性:因为不同平台对数据的在内存中的访问规则不同,不是所有的硬件都可以访问任意地址上的数据,某些硬件平台只能在特定的地址开始访问数据。所以需要内存对齐。
- 性能原因:一般使用内存对齐可以提高CPU访问内存的效率。如32位的intel处理器通过总线访问内存数据,每个总线周期从偶地址开始访问32位的内存数据,内存数据以字节为单位存放。如果32为的数据没有存放在4字节整除的内存地址处,那么处理器需要两个总线周期对数据进行访问,显然效率下降很多;另外合理的利用字节对齐可以有效的节省存储空间。
默认内存对齐影响因素:与平台架构(位数)和编译器的默认设置有关。
总线周期:CPU通过总线和存储器或者IO设备进行一次数据传输需要的时间,通常为四个或者多个时钟周期组成。
内存对齐规则
- 整体类型的对齐规则:若设置了内存对齐为m个字节,类中的最大成员的对齐字节为n,则该数据类型的对齐字节为p=min(m,n)。(一般32位机器的默认pack为4位;64位机器的默认pack为8位,程序中可以显式设置pack的大小)
- 类型中成员的对齐规则:类中的第一个成员放在offset为0的位置;对于其他成员,若设置了内存对齐为m个字节,假设该数据成员的对齐字节数(即当前成员所占的字节数)为k,则该数据成员的起始位置是min(m,k)的整数倍。
- 整体对齐规则:最后整个类型的大小为p=min(m,n)的整数倍。
- 当设置对齐字节数大于类中最大成员的对齐字节数的时候,这个设置实际不产生任何效果;当设置对齐字节数为1时,类的大小就是简单的把所有成员大小相加。
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <stddef.h> using namespace std; struct T { int a; short b; int c; double d; }; int main() { cout << offsetof(T, a) << endl << offsetof(T, b) << endl << offsetof(T, c) << endl << offsetof(T, d) << endl; return 0; }
|
根据上面的分析:
- a为第一个成员,offset为0。
- b为short,对齐字节数为2,所以其对齐字节为min(2,4)=2,故offset为4
- c为int,对齐字节数为4,所以其对齐字节数为min(4,4)=4,故offset为8
- d为double,对齐字节数为8,故对齐字节数为min(4,8)=8,故offset为12
- 总的大小为20,是,min(8,4)的倍数.
使用pragma pack修改系统默认pack
修改系统的默认pack可以使用系统函数pragma的pack参数,但是修改之后的pack一定是2的n次幂。
1 2 3
| #pragma pack(16) #pragma pack() #pragma pack(show)
|
此外pack还有push,pop其他参数可选,但是不同的编译器对这些参数的实现有不同的含义,如果需要了解可以参考对应的资料。
Comment and share