#pragma pack( n )和__declspec( align(#) ) 的偏移量计算方法

http://blog.csdn.net/DONALDO_O/archive/2008/11/15/3306588.aspx

 

 

这么复杂的东西,不用中文压根说不清楚。用英文写,写了也等写火星文。

 

现在的一些处理器,需要你的数据的内存地址必须是对齐( align )的,即使不是必须,如果你对齐的话,运行的速度也会得到提升。虽然对齐会产生的额外内存空间,但相对于这个速度的提升来说,是值得的。

所谓对齐,就是地址必须能整除一个整数,这个就是对齐参数( alignment value )。合法的取值范围是 1 2 4 6 16 、……、 8192

怎样对齐呢?编译器帮你搞定。

怎样设置编译器的对齐方式呢?用 #pragma pack( n ) __declspec(align(# ))

依据它俩,编译器是咋工作的?这个就是接下来要说的了。

 

#pragma pack( n )

不说别的,先直接举例说明这个 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.

翻译成中文,也就是:

结构体中的数据成员,除了第一个是始终放在最开始的地方,其它数据成员的地址必须是它本身大小或对齐参数两者中较小的一个的倍数。

 

按照这个理论,如果把上面的 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(#) )

__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 ),这个填充大小是成员变量最大的一个对齐参数或整个结构体的对齐参数的倍数,取哪个决定于哪个对齐参数较大

你可能感兴趣的:(c,工作,struct,编译器,alignment,structure)