您的位置:首页 > 其它

#pragma pack 用法详解

2011-06-29 00:00 281 查看
#pragma pack 用法详解

pack
为 struct, union和 class等的成员对齐指定字节边界.与编译选项(属性 ->配置属性
-> C/C++ ->代码生成 ->结构成员对齐)的 /Zp开关不同,它不针对整个项目,而仅针对模块,比如一个编译单元.

1. #pragma pack(show)

以警告信息的形式显示当前字节对齐的值.

2. #pragma pack(n)

将当前字节对齐值设为 n .

3. #pragma pack()

将当前字节对齐值设为默认值(通常是8) .

4. #pragma pack(push)

将当前字节对齐值压入编译栈栈顶.

5. #pragma pack(pop)

将编译栈栈顶的字节对齐值弹出并设为当前值.

6. #pragma pack(push, n)

先将当前字节对齐值压入编译栈栈顶, 然后再将 n
设为当前值.

7. #pragma pack(pop, n)

将编译栈栈顶的字节对齐值弹出, 然后丢弃,
再将 n 设为当前值.

8. #pragma pack(push, identifier)

将当前字节对齐值压入编译栈栈顶, 然后将栈中保存该值的位置标识为 identifier .

9. #pragma pack(pop, identifier)

将编译栈栈中标识为 identifier 位置的值弹出,并将其设为当前值.注意,如果栈中所标识的位置之上还有值,那会先被弹出并丢弃.

10. #pragma pack(push, identifier, n)

将当前字节对齐值压入编译栈栈顶, 然后将栈中保存该值的位置标识为 identifier,再将 n设为当前值.

11. #pragma pack(pop, identifier, n)

将编译栈栈中标识为 identifier 位置的值弹出,然后丢弃,再将 n设为当前值.注意,如果栈中所标识的位置之上还有值,那会先被弹出并丢弃.

注意: 如果在栈中没有找到 pop 中的标识符,则编译器忽略该指令,而且不会弹出任何值.

//代码段 1: 弹出编译栈的顺序跟压入的顺序相反

#pragma pack(show) // 8 (默认值)

#pragma pack(push, 16) // 默认值 8 压入编译栈栈顶,并将当前对齐值设为 16 .

#pragma pack(show) // 上句设定的 16

#pragma pack(push, 4) // 上上句 16 压入编译栈栈顶,并将当前对齐值设为 4 .

#pragma pack(show) // 上句设定的 4

#pragma pack(push, 2) // 上上句 4 压入编译栈栈顶,并将当前对齐值设为 2 .

#pragma pack(show) // 上句设定的 2

#pragma pack(push, 1) // 上上句 2 压入编译栈栈顶,并将当前对齐值设为 1 .

#pragma pack(show) // 上句设定的 1

#pragma pack(pop) // 弹出编译栈栈顶的 2 , 并将其设为当前对齐值.

#pragma pack(show) // 2

#pragma pack(pop) // 弹出编译栈栈顶的 4 , 并将其设为当前对齐值.

#pragma pack(show) // 4

#pragma pack(pop) // 弹出编译栈栈顶的 16 , 并将其设为当前对齐值.

#pragma pack(show) // 16

#pragma pack(pop) // 弹出编译栈栈顶的 8 , 并将其设为当前对齐值.

#pragma pack(show) // 8

//代码段 2: pop 带有参数 n 时,当前字节对齐值被设为了 n,而不是从栈顶弹出的之前所压入的值.

#pragma pack(show) // 8 (默认值)

#pragma pack(push, 16) // 默认值 8 压入编译栈栈顶,并将当前对齐值设为 16 .

#pragma pack(show) // 16

#pragma pack(push, 4) // 上上句 16 压入编译栈栈顶,并将当前对齐值设为 4 .

#pragma pack(show) // 4

#pragma pack(push, 2) // 上上句 4 压入编译栈栈顶,并将当前对齐值设为 2 .

#pragma pack(show) // 2

#pragma pack(push, 1) // 上上句 2 压入编译栈栈顶,并将当前对齐值设为 1 .

#pragma pack(show) // 1

#pragma pack(pop, 8) // 弹出编译栈栈顶的 2 , 然后丢弃,再将当前对齐值设为 8 .

#pragma pack(show) // 8

#pragma pack(pop, 1) // 弹出编译栈栈顶的 4 , 然后丢弃,再将当前对齐值设为 1 .

#pragma pack(show) // 1

#pragma pack(pop, 2) // 弹出编译栈栈顶的 16 , 然后丢弃,再将当前对齐值设为 2 .

#pragma pack(show) // 2

#pragma pack(pop, 16) // 弹出编译栈栈顶的 8 , 然后丢弃,再将当前对齐值设为 16 .

#pragma pack(show) // 16

//代码段3: push 和 pop 可以带有标识符,此标识符能够弹出指定值.但是,位于栈中指定值之上的那些值均会被弹出并丢弃.

#pragma pack(show) // 8 (默认值)

#pragma pack(push, identifier_1, 1) // 默认值 8 压入编译栈栈顶,并将栈中 8对应的位置用 identifier_1标识起来,然后将当前对齐值设为
1 .

#pragma pack(show) // 1

#pragma pack(push, identifier_2, 2) // 上上句 1 压入编译栈栈顶,并将栈中 1对应的位置用 identifier_2标识起来,然后将当前对齐值设为
2 .

#pragma pack(show) // 2

#pragma pack(push, identifier_3, 4) // 上上句 2 压入编译栈栈顶,并将栈中 2对应的位置用 identifier_3标识起来,然后将当前对齐值设为
4 .

#pragma pack(show) // 4

#pragma pack(push, identifier_4, 8) // 上上句 4 压入编译栈栈顶,并将栈中 4对应的位置用 identifier_4标识起来,然后将当前对齐值设为
8 .

#pragma pack(show) // 8

#pragma pack(push, identifier_5, 16) // 上上句 8 压入编译栈栈顶,并将栈中 8对应的位置用 identifier_5标识起来,然后将当前对齐值设为
16 .

#pragma pack(show) // 16

#pragma pack(push, identifier_6) // 上上句 16 压入编译栈栈顶,并将栈中 16对应的位置用 identifier_6标识起来.

#pragma pack(show) // 16

#pragma pack(pop, identifier_6) // 将标识符 identifier_6对应的栈中值 16弹出,并将其设为当前对齐值.

#pragma pack(show) // 16

#pragma pack(pop, identifier_5, 2) // 将标识符 identifier_6对应的栈中值 8弹出,然后丢弃,再将当前对齐值设为 2 .

#pragma pack(show) // 2

#pragma pack(pop, identifier_1) // 按入栈顺序进行弹出,
直到遇到标识符 identifier_1 标识的8 .

#pragma pack(show) // 8

//////////////////////////////////////////////////////////////////////////////////////////////////////
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

例如,下面的结构各成员空间分配情况:

struct test

{

char x1;

short x2;

float x3;

char x4;

};

结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节
对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面
填充了3个空字节。整个结构所占据空间为12字节。

//////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma pack(8)

struct S1{

short a;

long b;

};

struct S2{

char c;

s1 d;

long long e;

};

#pragma pack()



1.sizeof(S2) = ?

2.s2的c后面空了几个字节接着是d?

结果如下:

sizeof(S2)结果为24.

成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.

也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.

S1中,成员a是2字节默认按2字节对齐,指定对齐参数为8,这两个值中取2,a按2字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;

S2 中,c和S1中的a一样,按1字节对齐,而d是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是
按4字节对齐.成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节
的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e按8字节对齐)整除.这样,一共使用了24个字节.

a b

S1的内存布局:11**,1111,

c S1.a S1.b d

S2的内存布局:1***,11**,1111,****11111111

这里有三点很重要:

1.每个成员分别按自己的方式对齐,并能最小化长度

2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度

3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐

//////////////////////////////////////////////////////////////////////////////////////////////////////

取出结构中成员变量偏移位置的宏定义

#define OFFSET_OF_STRUCT(ty,var)
((int)(&((ty*)NULL)->var))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: