现在的位置: 首页 > 综合 > 正文

关于内存对齐的全面详细解释

2013年07月17日 ⁄ 综合 ⁄ 共 4150字 ⁄ 字号 评论关闭

一、内存对齐的原因
1、平台原因(移植原因):内存对齐其实不是操作系统内存架构范畴的问题,而是CPU架构方面的问题。不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。x86和x64系统支持自动内存对齐,IA-64系统则不能访问未对齐的内存地址。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

二、对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16...8192来改变这一系数,其中的n就是你要指定的“对齐系数”。关键字__declspec(align(n)),n=1,2,4,8,16...8192也能改变具体变量的对齐方式。

可以这样理解:
1、__declspec(align(n))和#pragma pack(n)是一对兄弟,前者规定了对齐系数的最小值,后者规定了对齐系数的最大值。
2、当两者同时出现时,前者拥有更高的优先级。即,当两者同时出现且值矛盾时,后者将不起作用。
3、当变量size大于等于#pragma pack(n)指定的n,而且__declspec(align(n))指定的数值n比对应类型长度小的时候,这个__declspec(align(n))指定将不起作用。
4、当#pragma pack(n)指定的值n大于等于所有数据成员size的时候,这个值n将不起作用。

简单数据成员对齐规则:
对齐系数 = max(__declspec(align(n))指定的n,min(自身size,#pragma pack(m)指定的m))。
结构型数据成员对齐规则:
对齐系数 = max(__declspec(align(n))指定的n,min(max(所有简单数据成员的size,包括嵌套),#pragma pack(m)指定的m))。
结构整体对齐规则:
对齐系数 = max(max(修饰该结构或修饰该结构成员的所有__declspec(align(n))指定的n,包括嵌套),min(max(所有简单数据成员的size,包括嵌套),#pragma pack(m)指定的m))。

上述规则看似很复杂,其实可总结为:
    对于成员对齐,第一个数据成员始终放在偏移为0的地方,在没有被__declspec(align(n))修饰时,以后每个数据成员的对齐,按照#pragma pack(n)指定的数值n和这个数据成员自身size中,比较小的那个进行;当被__declspec(align(n))修饰时,再使上述结果与__declspec(align(n))指定的n进行比较,然后按较大的那个进行。如果成员本身又是结构,则找到该结构中所有简单成员的size最大值,然后与#pragma pack(n)指定的数值n进行比较,取较小的那个;如果该结构成员本身被__declspec(align(n))修饰,再使上述结果与__declspec(align(n))指定的n进行比较,然后按较大的那个进行。
    对于结构整体的对齐,取结构中所有简单成员的size的最大值(如果有结构型成员,则将其展开,取其中的简单数据成员),然后与#pragma pack(n)指定的数值n进行比较,取较小者;如果结构或结构成员(包括嵌套)被__declspec(align(n))修饰,则取所有__declspec(align(n))(包括嵌套)指定的n的最大值,然后与上述得到的较小者进行比较,取其中的较大者。

三、试验
我们通过一系列例子的详细说明来证明这个规则吧!

我们首先确认在试验平台上的各个类型的size,以及默认对齐系数:
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
sizeof(double) = 8

#pragma pack(show) = 8 (以警告的方式显示编译器当前的默认对齐系数)

例1:
1)成员数据对齐:
#pragma pack(1)
struct Str{
char a; /* 起始offset=0;存放位置区间[0] */
short b; /* min(2,1)=1 按1对齐;起始offset=1 1%1=0;存放位置区间[1,2] */
__declspec(align(8)) int c; /* max(8,min(4,1)) 按8对齐;起始offset=3 3%8=!0;直到增加为8 8%8=0;存放位置区间[8...11] */
double d; /* min(8,1)=1 按1对齐;起始offset=12 12%1=0;存放位置区间[12...19] */
char e; /* min(1,1)=1 按1对齐;起始offset=20 20%1=0;存放位置区间[20] */
};
成员总偏移量 = 21
2)整体对齐
整体对齐系数 = max(max(8),min(max(1,2,4,8,1),1)) = 8
21%8!=0;直到增加为24 24%8=0;所以整体的size = 24

例2:
1)成员数据对齐:
#pragma pack(16)
struct Str{
char a; /* 起始offset=0;存放位置区间[0] */
short b; /* min(2,16)=2 按2对齐;起始offset=1 1%2=!0;直到增加为2 2%2=0;存放位置区间[2,3] */
__declspec(align(2)) double c; /* max(2,min(8,16)) 按8对齐;起始offset=4 4%8=!0;直到增加为8 8%8=0;存放位置区间[8...15] */
int d; /* min(4,16)=4 按4对齐;起始offset=16 16%4=0;存放位置区间[16...19] */
};
成员总偏移量 = 20
2)整体对齐
整体对齐系数 = max(max(2),min(max(1,2,8,4),16)) = 8
20%8!=0;直到增加为24 24%8=0;所以整体的size = 24

例3:
1)成员数据对齐:
struct Str{
__declspec(align(16)) char a; /* 起始offset=0;存放位置区间[0] */
short b; /* min(2,8)=2 按2对齐;起始offset=1 1%2=!0;直到增加为2 2%2=0;存放位置区间[2,3] */
__declspec(align(2)) double c; /* max(2,min(8,8)) 按8对齐;起始offset=4 4%8=!0;直到增加为8 8%8=0;存放位置区间[8...15] */
int d; /* min(4,8)=4 按4对齐;起始offset=16 16%4=0;存放位置区间[16...19] */
};
成员总偏移量 = 20
2)整体对齐
整体对齐系数 = max(max(16,2),min(max(1,2,8,4),8)) = 16
20%16!=0;直到增加为32 32%16=0;所以整体的size = 32

例4:
1)成员数据对齐:
#pragma pack(1)
struct Str{
char a; /* 起始offset=0;存放位置区间[0] */
__declspec(align(2)) short b; /* max(2,min(2,1)) 按2对齐;起始offset=1 1%2=!0;直到增加为2 2%2=0;存放位置区间[2,3] */
};
struct Str总偏移量 = 4,按max(max(2),min(max(1,2),1))进行整体对齐后,其size为4。

struct Str2{
char a; /* 起始offset=0;存放位置区间[0] */
struct Str b; /* min(max(1,1,2),1) 按1对齐;起始offset=1 1%1=0;存放位置区间[1...4] */
};

struct Str2总偏移量 = 5
2)整体对齐
整体对齐系数 = max(max(2),min(max(1,1,2),1)) = 2
5%2!=0;直到增加为6 6%2=0;所以整体的size = 6

简单数据成员对齐规则:
对齐系数 = max(__declspec(align(n))指定的n,min(自身size,#pragma pack(m)指定的m))。
结构型数据成员对齐规则:
对齐系数 = max(__declspec(align(n))指定的n,min(max(所有简单数据成员的size,包括嵌套),#pragma pack(m)指定的m))。
结构整体对齐规则:
对齐系数 = max(max(修饰该结构或修饰该结构成员的所有__declspec(align(n))指定的n,包括嵌套),min(max(所有简单数据成员的size,包括嵌套),#pragma pack(m)指定的m))。

例5:
1)成员数据对齐:
struct Str{
char a; /* 起始offset=0;存放位置区间[0] */
__declspec(align(16)) short b; /* max(16,min(2,8)) 按16对齐;起始offset=1 1%16=!0;直到增加为16 16%16=0;存放位置区间[16,17] */
};
struct Str总偏移量 = 18,按max(max(16),min(max(1,2),8))进行整体对齐后,其size为32。

struct Str2{
int a; /* 起始offset=0;存放位置区间[0...3] */
__declspec(align(8)) struct Str b; /* max(8,min(max(1,2),8)) 按8对齐;起始offset=4 4%8=!0;直到增加为8 8%8=0;存放位置区间[8...39] */
double c; /* min(8,8)=8 按8对齐;起始offset=40 40%8=0;存放位置区间[40...47] */
char d; /* min(1,8)=1 按1对齐;起始offset=48 48%1=0;存放位置区间[48] */
};

struct Str2总偏移量 = 49
2)整体对齐
整体对齐系数 = max(max(8,16),min(max(4,1,2,8,1),1)) = 16
49%16!=0;直到增加为64 64%16=0;所以整体的size = 64

抱歉!评论已关闭.