您的位置:首页 > 其它

#pragma pack(n) 对齐用法详解

2011-06-14 13:59 447 查看
#pragma pack(n) 对齐用法详解(转载)

原文地址 http://www.360doc.com/showWeb/0/0/16446681.aspx

什么是对齐,以及为什么要对齐:
现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个 int 型(假设为 32 位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要 2 个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该 int 数据。显然在读取效率上下降很多。这也是空间和时间的博弈。
对齐的实现
通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择时候目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。
但是,正因为我们一般不需要关心这个问题,所以因为编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是 struct 数据结构的 sizeof 结果,出乎意料。为此,我们需要对对齐算法所了解。

作用:
指定结构体、联合以及类成员的 packing alignment;

语法:
#pragma pack( [show] | [push | pop] [, identifier], n )

说明:
1 , pack 提供数据声明级别的控制,对定义不起作用;
2 ,调用 pack 时不指定参数, n 将被设成默认值;
3 ,一旦改变数据类型的 alignment ,直接效果就是占用 memory 的减少,但是 performance 会下降 ;

语法具体分析:
1 , show :可选参数;显示当前 packing aligment 的字节数,以 warning message 的形式被显示;
2 , push :可选参数;将当前指定的 packing alignment 数值进行压栈操作,这里的栈是 the internal compiler stack ,同时设置当前的 packing alignment 为 n ;如果 n 没有指定,则将当前的 packing alignment

>数值压栈;
3 , pop :可选参数;从 internal compiler stack 中删除最顶端的 record ;如果没有指定 n ,则当前栈顶 record 即为新的 packing alignment 数值;如果指定了 n ,则 n 将成为新的 packing aligment 数值;如果指定了 identifier ,则 internal compiler stack 中的 record 都将被 pop 直到 identifier 被找到,然后 pop 出 identitier ,同时设置 packing alignment 数值为当前栈顶的 record ;如果指定的 identifier 并不存在于 internal compiler stack ,则 pop 操作被忽略;
4 , identifier :可选参数;当同 push 一起使用时,赋予当前被压入栈中的 record 一个名称;当同 pop 一起使用时,从 internal compiler stack 中 pop 出所有的 record 直到 identifier 被 pop 出,如果 identifier 没有被找到,则忽略 pop 操作;
5 , n :可选参数;指定 packing 的数值,以字节为单位;缺省数值是 8 ,合法的数值分别是 1 、 2 、 4 、 8 、 16 。

重要规则:
1 ,复杂类型中各个成员按照它们被 声明的顺序 在内存中 顺序存储 ,第一个成员的地址和整个类型的地址相同;
2 ,每个成员分别对齐,即每个成员按自己的方式对齐,并最小化长度;规则就是每个成员按其类 型的对齐参数( 通常是这个类型的大小 )和 指定对齐参数 中 较小 的一个对齐;
3 ,结构、联合或者类的数据成员,第一个放在偏移为 0 的地方;以后每个数据成员的对齐,按照 #pragma pack 指定的数值和这个数据成员自身长度两个中比 较小 的那个进行;也就是说,当 #pragma pack 指定的值等于或者超过所有数据成员长度的时候,这个指定值的大小将不产生任何效果;
4 ,复杂类型(如结构) 整体的对齐 < 注意是“整体” > 是按照结构体中 长度最大的数据成员 和 #pragma pack 指定值 之间 较小 的那个值进行;这样在成员是复杂类型时,可以最小化长度;
5 ,结构整体长度的计算必须取所用过的所有对齐参数的整数倍,不够补空字节;也就是取所用过的所有对齐参数中最大的那个值的整数倍,因为对齐参数都是 2 的 n 次方;这样在处理数组时可以保证每一项都边界对齐;

对齐的算法:
由于各个平台和编译器的不同,现以本人使用的 gcc version 3.2.2 编译器( 32 位 x86 平台)为例子,来讨论编译器对 struct 数据结构中的各成员如何进行对齐的。

在 相同的对齐方式下 ,结构体内部数据 定义的顺序不同 ,结构体整体占据内存空间也 不同 ,如下:
设结构体如下定义:
struct A
{
int a;
char b;
short c;
};
结构体 A 中包含了 4 字节长度的 int 一个, 1 字节长度的 char 一个和 2 字节长度的 short 型数据一个。所以 A 用到的空间应该是 7 字节。但是因为编译器要对数据成员在空间上进行对齐。所以使用 sizeof(strcut A) 值为 8 。 < /font>
现在把该结构体调整成员变量的顺序。
struct B
{
char b;
int a;
short c;
};
这时候同样是总共 7 个字节的变量,但是 sizeof(struct B) 的值却是 12 。
下面我们使用预编译指令 #pragma pack (value) 来告诉编译器,使用我们指定的对齐值来取代缺省的。
#pragma pack (2) /* 指定按 2 字节对齐, 等价于 #pragma pack(push,2) */
struct C
{
char b;
int a;
short c;
};
#pragma pack () /* 取消指定对齐,恢复缺省对齐 , 等价于 #pragma pack(pop) */
sizeof(struct C) 值是 8 。

修改对齐值为 1 :
#pragma pack (1) /* 指定按 1 字节对齐 */
struct D
{
char b;
int a;
short c;
};
#pragma pack () /* 取消指定对齐,恢复缺省对齐 */
sizeof(struct D) 值为 7
对于 char 型数据,其自身对齐值为 1 ,对于 short 型为 2 ,对于 int,float,double 类型,其自身对齐值为 4 ,单位字节。

这里面有四个概念值:
1. 数据类型 自身的对齐值 :就是上面交代的基本数据类型的自身对齐值。
2. 指定对齐值 : #pragma pack (value) 时的指定对齐值 value 。
3. 结构体或者类 的自身对齐值:其 数据成员中 自身对齐值 最大 的那个值。
4. 数据成员、结构体和类的 有效对齐值 :自身对齐值和指定对齐值中 小 的那个值。
有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值 N 是最终用来决定数据存放地址方式的值,最重要。 有效对齐 N ,就是表示“对齐在 N 上”, 也就是说该数据的 " 存放起始地址 %N=0 ". 而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整 ( 就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解 ) 。这样就不能理解上面的几个例子的值了。
例子分析:
分析例子 B ;
struct B
{
char b;
int a;
short c;
};
假设 B 从地址空间 0x0000 开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值 默认为 4 。

第一个成员变量 b 的自身对齐值是 1 ,比指定或者默认指定对齐值 4 小,所以其有效对齐值为 1 ,所以其存放地址 符合 0x0000%1=0 .

第二个成员变量 a ,其自身对齐值为 4 ,所以有效对齐值也为 4 ,所以只能存放在起始地址为 0x0004 到 0x0007 这四个连续的字节空间中,符合 0x0004%4=0 , 且紧靠第一个变量。

第三个变量 c, 自身对齐值为 2 ,所以有效对齐值也是 2 ,可以存放在 0x0008 到 0x0009 这两个字节空间中,符合 0x0008%2=0 。所以从 0x0000 到 0x0009 存放的都是 B 内容。

再看数据结构 B 的自身对齐值 为其变量中最大对齐值 ( 这里是 b )所以就是 4 ,所以结构体的有效对齐值也是 4 。根据结构体圆整的要求, 0x0009 到 0x0000=10 字节 ,( 10 + 2 )% 4 = 0 。所以 0x0000A 到 0x000B 也为结构体 B 所占用。故 B 从 0x0000 到 0x000B 共有 http://msnpiki.msnfanatic.com/index.php/Main_Page--> 9pt;" size="2">12 个字节 , sizeof(struct B)=12 ;

同理 , 分析上面例子 C :
#pragma pack (2) /* 指定按 2 字节对齐 */
struct C
{
char b;
int a;
short c;
};
#pragma pack () /* 取消指定对齐,恢复缺省对齐 */
第一个变量 b 的自身对齐值为 1 ,指定对齐值为 2 ,所以,其有效对齐值为 1 ,假设 C 从 0x0000 开始,那么 b 存放在 0x0000 ,符合 0x0000%1=0;

第二个变量,自身对齐值为 4 ,指定对齐值为 2 ,所以有效对齐值为 2 ,所以顺序存放在 0x0002 、 0x0003 、 0x0004 、 0x0005 四个连续字节中,符合 0x0002%2=0 。

第三个变量 c 的自身对齐值为 2 ,所以有效对齐值为 2 ,顺序存放在 0x0006 、 0x0007 中,符合 0x0006%2=0 。所以从 0x0000 到 0x00007 共八字节存放的是 C 的变量。

又 C 的自身对齐值为 4 ,所以 C 的有效对齐值为 2 。又 8%2=0,C 只占用 0x0000 到 0x0007 的八个字节。所以 sizeof(struct C)=8.

1 /*================================================================
2 *Name: /arm/2440_root/app/gnu_test/test01.c
3 *Made by: zhangshengheng @ Mon Feb 22 15:06:07 HKT 2010
4 *Email: zhangshengheng@126.com
5 *FUNC: the functio

n of this program is :
6 #pragma pack(n)对齐用法详解
7 sizeof(A)=8
8 sizeof(B)=12
9 sizeof(C)=8
10 sizeof(D)=7
11 sizeof(E)=7
12 sizeof(F)=7
13 sizeof(G)=9
14
15 ================================================================*/
16 # include < stdio. h>
17 # include < stdlib. h>
18 # include < string . h>
19
20 //A和B元素的位置不一样,大小也不一样

21 struct A {
22 int a;
23 char b;
24 short c;
25 } ; //8

26 struct B {
27 char b;
28 int a;
29 short c;
30 } ; //12

31
32
33 # pragma pack( 2) //指定按照2字节对齐

34 struct C { //等价于#pragma pack(push, 2)

35 char b;
36 int a;
37 short c;
38 } ;
39 # pragma pack( ) //8 //取消指定对齐,恢复缺省对齐

40 //等价于#pragma pack(pop)

41
42 # pragma pack( 1)
43 struct D {
44 char b;
45 int a;
46 short c;
47 } ;

48 # pragma pack( ) //7

49
50
51 # pragma pack( 1) //这个的作用范围是一直向下

52 struct E {
53 char b;
54 int a;
55 short c;
56 } ; //7

57 struct F {
58 int a;
59 char b;
60 short c;
61 } ; //7

62 struct G {
63 int a;
64 char b;
65 int c;
66 } ; //9

67
68
69
70 int main( void )
71 {
72
73 printf ( "sizeof(A)=%d/n" , sizeof ( struct A) ) ;

74 printf ( "sizeof(B)=%d/n" , sizeof ( struct B) ) ;
75 printf ( "sizeof(C)=%d/n" , sizeof ( struct C) ) ;
76 printf ( "sizeof(D)=%d/n" , sizeof ( struct D) ) ;
77 printf ( "sizeof(E)=%d/n" , sizeof ( struct E) ) ;
78 printf ( "sizeof(F)=%d/n" , sizeof ( struct F) ) ;
79 printf ( "sizeof(G)=%d/n" , sizeof ( struct G) ) ;
80
81 return 0;
82
83 }

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/shark0001/archive/2010/03/26/5414876.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: