#pragma pack 用法详解

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

例如,下面的结构各成员空间分配情况:
struct test
{
     char x1;
     short x2;
     float x3;
     char x4;
};

结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2short类型,其起始地址必须2字节 对界,因此,编译器在x2x1之间填充了一个空字节。结构的第三个成员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
,成员a2字节默认按2字节对齐,指定对齐参数为8,这两个值中取2,a2字节对齐;成员b4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为
8;
S2
,cS1中的a一样,1字节对齐,d是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是 按4字节对齐.成员e8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节 的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e8字节对齐)整除.这样,一共使用了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))

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值