内存对齐

    技术2022-05-20  52

          本文只是为了方便自己学习对内存对齐的相关知识进行了总结,其中参考了很多BLOG、WIKI、BAIDU文库等别人的知识,部分内容是直接拷贝的!

    感谢http://blog.csdn.net/hkx1n/archive/2009/06/16/4274309.aspx

          http://blog.csdn.net/Microsues/archive/2011/01/14/6140329.aspx

          http://blog.csdn.net/hairetz/archive/2009/04/16/4084088.aspx

     

     

    一:内存对齐的由来

         从程序员的角度看:内存可以看成一个byte数组,我们通过编程语言提供的工具对这个'大数组'中的每个元素进行读写,比如在C中我们可以用指针一次读写一个或者更多个字节。从CPU角度看:CPU是按照'块(chunk)'来读写内存的,块的大小可以是2bytes, 4bytes, 8bytes, 16bytes甚至是32bytes. 这个CPU访问内存采用的块的大小,我们可以称为'内存访问粒度'。 程序员眼中的内存样子: -----------------------------------------  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | -----------------------------------------  0 1 2 3 4  5 6 7  8 9 A B C D E  F  (地址) CPU眼中的内存样子:(以粒度=4为例) --------------------------------------------- | | | | |       | | | | |     | | | | |    | | | | | ---------------------------------------------  0 1 2 3     4 5 6 7    8 9 A B   C D E F  (地址) 有了上面的概念,我们来看看粒度对CPU访问内存的影响,假设这里我们需要的数据分别存储于地址0和地址1起始的连续4个字节的存储器中,我们目的是分别读取这些数据到一个4字节的寄存器中。

    1)如果'内存访问粒度'为1,CPU从地址0开始读取,需要4次访问才能将4个字节读到寄存器中;

    2)如果'内存访问粒度'为1,CPU从地址1开始读取,也需要4次访问才能将4个字节读到寄存器中;而且对于这种理想中的''内存访问粒度'为1的CPU,所有地址都是"aligned address"。

    3) 如果'内存访问粒度'为2,CPU从地址0开始读取,需要2次访问才能将4个字节读到寄存器中;每次访存都能从'aligned address'起始。

    4)如果'内存访问粒度'为2,CPU从地址1开始读取,相当于内存中数据分布在1,2,3,4三个地址上,由于1不是'aligned address',所以这时CPU要做些其他工作,由于这四个字节分步在三个chunk上,所以CPU需要进行三次访存操作,第一次读取chunk1(即地址0,1上两个字节,而且仅仅地址1上的数据有用),第二次读取chunk2(即地址2,3上两个字节,这两个地址上的数据都有用),最后一次读取chunk3(即地址5,6上两个字节,而且仅仅地址5上的数据有用),最后CPU会将读取的有用的数据做merge操作,然后放到寄存器中。 同理可以推断如果'内存访问粒度'为4,那么从地址1开始读取,需要2次访问,访问后得到的结果merge后放到寄存器中。 一般编译器都会在编译时做相应的优化以保证程序运行时所有数据都是存储在'aligned address'上的,主要为了提高读取的速度,这就是内存对齐的由来。

    二:内存对齐的规则

         说明:内存对齐指结构struct中每个数据成员分别按自己的方式对齐,也就是从某个值K的整数倍开始存放。K的大小和两个值有关:数据成员本身的大小m(比如sizeof(int)=4,int型的数据成员大小m为4)、 #pragma pack(n)宏(Gcc中为__attribute__(aligned(n)) )大小。 Vc等编译器默认是#pragma pack(8),gcc默认是#pragma pack(4),并且gcc只支持1,2,4对齐。K取两者之间较小的,即K=min(m,n)。当n=1时候,就没有所谓的内存对齐了,n其实控制了对齐的粒度。

    1、结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方。 以后每个数据成员存储的起始位置要从K的整数倍开始(比如int在32位机为4字节,如果n=8, 则要从4的整数倍地址开始存储).

    2、结构体作为成员:如果一个结构里有某些结构体成员,则该结构体成员的m值取其内部"最宽基本类型成员"( 基本类型不包括struct/class/uinon )的大小,再计算其K的大小.(struct a里存有struct b,b里有char,int ,double等元素,那b的m值大小为8)。

    3、 结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack(n)指定的数值n和结构(或联合)中 " 最宽基本类型成员"值 ,比较小的那个进行。 也就是说sizeof(struct_t)值 其内部"最宽基本类型成员"值和 #pragma pack(n)所指定的n 中较小的值 的整数倍.不足的要补齐.

    4、sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址

     

    5位域,如果相邻位域字段的类型相同,且其位宽之和小于该类型(比如整型为sizeof(int))大小,则  后面的字段将紧邻前一个字段存储,直到不能容纳为止,这个几个字段以一个整体进行对齐; 如果相邻位域字段的类型相同,但其位宽之和大于类型的大小,则后面的字段将从新的存储单元开始。

     

    三:内存对齐实例

    实例直接取自 http://blog.csdn.net/Microsues/category/757934.aspx

    1:#pragma pack(8)

    #include <iostream> #pragma pack(8) using namespace std; union a { int a_int1; //4字节 double a_double; //8字节 int a_int2; //4字节 char ch; //1字节 }; //由原则4,a的大小为8 struct b { a a1; //[0]...[7] , a的大小为8 char y; //[8] }; //由原则3,"最宽基本类型成员"为a中的double,n=8 //从而b的大小为8的整数倍,所以b的大小为16 class c { int c_double; //[0]...[3] b b1; //[8]...[23] 原则2:k=min(sizeof(double,8))的整数倍开始存储 char ch; //[24] //由原则3,"最宽基本类型成员"为b中,即b里面a的double,n=8 //c的大小为8的整数倍,所以c的大小为32 }; struct d { char ch; //[0] b b2; //[8]...[23] 原则2:k=min(sizeof(double,8))整数倍开始存储 a a2; //[24]...[31] 原则2:k=min(sizeof(double,8))整数倍开始存储 c c2; //[32]...[63] 原则2:k=min(sizeof(double,8))整数倍开始存储 char ch1; //[64] //"最宽基本类型成员"double ,n=8 //由原则3,d的大小为8的整数倍,所以d的大小为72 }; void main() { cout<<"sizeof(a)= "<<sizeof(a)<<endl; cout<<"sizeof(b)= "<<sizeof(b)<<endl; cout<<"sizeof(c)= "<<sizeof(c)<<endl; cout<<"sizeof(d)= "<<sizeof(d)<<endl; }

     

    2:#pragma pack(4)

    #include <iostream> #pragma pack(4) using namespace std; union a { int a_int1; //4字节 double a_double; //8字节 int a_int2; //4字节 char ch; //1字节 }; //由原则4,a的大小为8 struct b { a a1; //[0]...[7] , a的大小为8 char y; //[8] }; //由原则3,"最宽基本类型成员"为a中的double,n=4 //从而b的大小为4的整数倍,所以b的大小为12 class c { int c_double; //[0]...[3] b b1; //[4]...[15] 原则2:k=min(sizeof(double,4))的整数倍开始存储 char ch; //[16] //由原则3,"最宽基本类型成员"为b中,即b里面a的double,n=4 //c的大小为4的整数倍,所以c的大小为20 }; struct d { char ch; //[0] b b2; //[4]...[15] 原则2:k=min(sizeof(double,4))整数倍开始存储 a a2; //[16]...[23] 原则2:k=min(sizeof(double,4))整数倍开始存储 c c2; //[24...[43] 原则2:k=min(sizeof(double,4))整数倍开始存储 char ch1; //[44] //"最宽基本类型成员"double ,n=4 //由原则3,d的大小为4的整数倍,所以d的大小为48 }; void main() { cout<<"sizeof(a)= "<<sizeof(a)<<endl; cout<<"sizeof(b)= "<<sizeof(b)<<endl; cout<<"sizeof(c)= "<<sizeof(c)<<endl; cout<<"sizeof(d)= "<<sizeof(d)<<endl; }

     

     


    最新回复(0)