这么复杂的东西,不用中文压根说不清楚。用英文写,写了也等写火星文。
现在的一些处理器,需要你的数据的内存地址必须是对齐(align)的,即使不是必须,如果你对齐的话,运行的速度也会得到提升。虽然对齐会产生的额外内存空间,但相对于这个速度的提升来说,是值得的。
所谓对齐,就是地址必须能整除一个整数,这个就是对齐参数(alignment value)。合法的取值范围是1、2、4、6、16、……、8192。
怎样对齐呢?编译器帮你搞定。
怎样设置编译器的对齐方式呢?用#pragma pack( n )和__declspec(align(#))。
依据它俩,编译器是咋工作的?这个就是接下来要说的了。
但要说清楚的是,以下内容都是本人依据MSDN、网上文章和运行结果自己所推敲出来的,不能确保其一定正确。推荐自己手动做几个实验对其进行验证。
不说别的,先直接举例说明这个n是这么用的。
#pragma pack( 4 )
struct A
{
char a;
short b;
char c;
};
分配方法:
ü a是第一个,占[0]
ü short大小是2,2比n=4小,用2对齐,即b的地址必须是2的倍数,所以[1]不用,b占[2]、[3]
ü char大小是1,1比n=4小,用1对齐,即c占[4]
ü 算完各数据成员后,总共5B。最后,因为结构体A中最大的元素是2B的short,2比n=4小,保证A的大小可以整除2,所以最后结果是6。
ü 整个struct,内存分配情况是:a占[0],b占[2][3],c占[4],[1][5]不用。
用MSDN的话一言以蔽之:
“The alignment of a member (except the first one) will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.”
翻译成中文,也就是:
“结构体中的数据成员,除了第一个是始终放在最开始的地方,其它数据成员的地址必须是它本身大小或对齐参数两者中较小的一个的倍数。”
用数学表达式表示:
AlignedValue=min(sizeof(DataMember),n)
DataMemberAddr=AlignedValue*i,i属于Z*,且i是可以使DataMemberAddr合法(即保证不跑到上一个数据成员的地址中去)的最小一个Z*。
如果你被这些定义搞晕了,你完全可以不理这些定义,只要记住分配方法就行了。
按照这个理论,如果把上面的A的数据成员的顺序换一换的话,出现什么情况呢?
例如:
#pragma pack( 4 )
struct A
{
char a;
char c;
short b;
};
分配方法:
ü a是第一个,占[0]
ü char大小是1,1比n=4小,用1对齐,所以c占[1]
ü short大小是2,2比n=4小,用2对齐,即b占[2][3]
ü 算完各数据成员后,总共4B。最后,因为结构体A中最大的元素是2B的short,2比n=4小,保证A的大小可以整除2,所以最后结果是4。
奇怪吧,改变数据成员的顺序是可以改变结构体的大小的。
特别提出:
ü A ao;
sizeof(ao.a )还是1,sizeof(ao.b )还是2。
ü 如果struct B中含有A的一个对象m_a,
struct B
{
…
A m_a;
…
}
则这个m_a对齐参数是A中最大的数据类型的大小(这里是short的2)和n中较小者。如果这个对齐参数是B中最大的话,最后B的大小也会与这个对齐参数有关。
__declspec( align(#) )和#pragma pack( n )有密切联系。
当一个变量或结构体同时受两者影响时,前者的优先级高。
成员的地址决定于前者及后者,其要么是前者的倍数,要么是后者的倍数,要么是成员的大小的倍数,取最小。
结构体最后的大小于前者有关,其要么是前者的倍数,要么是结构体中最大偏移量的倍数,取最大。
要算出最后结果,必须知道两者的值或缺省值。
惯例,直接给例子:
#pragma pack( push, 4 )
__declspec( align(32) )struct D
{
Int i1;
double d1;
Int i2;
Int i3;
};
ü I1是第一个,占[0][1][2][3]
ü double大小是8,8比n=4大,用4对齐,所以d1占[4][5][6][7][8][9][10][11]。
ü int大小是2,2比n=4小,用2对齐,即i2占[12][13][14][15]
ü int大小是2,2比n=4小,用2对齐,即i3占[16][17][18][19]
ü 到现在为止,计算方法都和上面的几个例子完全一样。现在算出成员总共占了20B,而成员中最大的偏移量是4。
ü 当计算结构体的最后大小时,因为结构体接受__declspec( align(32) )影响,比较4和#,4比#=32小,用32补完,所以最后的大小应该是大于20B最小的32倍数,即32B。
现在再定义含有D的E
__declspec( align(16) ) struct E
{
int i1;
D m_d;
int i2;
};
#pragma pack( pop )
ü I1是第一个,占[0][1][2][3]
ü m_d受之前的__declspec( align(32) )影响,优先级高,偏移量是32,用32对齐,所以d1占[32]-[63]。
ü int大小是2,2比n=4小,用2对齐,即i2占[64][65][66][67]
ü 现在算出成员总共占了68B,而成员中最大的偏移量是32。
ü 当计算结构体的最后大小时,因为结构体接受__declspec( align(16) )影响,比较32和#,32比#=16大,用32补完,所以最后的大小应该是大于68B最小的32倍数,即96B。
MSDN:
“The sizeof value for any structure is the offset of the final member, plus that member's size, rounded up to the nearest multiple of the largest member alignment value or the whole structure alignment value, whichever is greater.”
中文:
“sizeof的结果都是结构体中最后的一个成员变量加上它的大小,再加上一个填充容量(padding),这个填充大小是成员变量最大的一个对齐参数或整个结构体的对齐参数的倍数,取哪个决定于哪个对齐参数较大”
参考:
MSDN:
ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vclang/html/e4209cbb-5437-4b53-b3fe-ac264501d404.htm
ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vclang/html/9cb63f58-658b-4425-ac47-af8eabfc5878.htm
网站:
http://blog.sina.com.cn/s/blog_492aa57901008y3h.html