[牛人杂谈] C #pragma pack(push,1) #pragma pack(pop)解析

[复制链接]
1179|9
 楼主| dongnanxibei 发表于 2019-12-7 15:48 | 显示全部楼层 |阅读模式
一、结构
  1. #ifdef _WIN32
  2. #pragma pack( push, 1 )
  3. #else
  4. #pragma pack(1)
  5. #endif

  6. Typedef  struct

  7. {

  8. }

  9. #ifdef _WIN32
  10. #pargma pack(pop)
  11. #else
  12. #pragma pack()
  13. #endif
二、对 #pragma pack()的理解
在程序中,我们有时候在定义结构体时,需要使用 #pargma pack(push,1) 和 #pragma pack(pop) 类似代码将结构体包裹起来,形式如上。

#pragma pack是指定数据在内存中的对齐方式

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

如果不用#pargma pack()包裹,则结构体按编译器默认对其方式(成员中size最大的那个)对齐。这里包括以下三个原则:

原则1:数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

原则2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

原则3:收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。



 楼主| dongnanxibei 发表于 2019-12-7 15:49 | 显示全部楼层
三、示例
  1. #include<stdio.h>

  2. struct test
  3. {
  4. char x1;
  5. float x3;
  6. short x2;
  7. char x4;
  8. };

  9. struct test2
  10. {
  11. char x1;
  12. double x2;
  13. short x3;
  14. char x4;
  15. short x5;
  16. struct test t;
  17. };
  18. void main(void)
  19. {
  20. struct test t;
  21. struct test2 t2;
  22. int len;
  23. int len2;
  24. len=sizeof(t);
  25. len2=sizeof(t2);
  26. setvbuf(stdout,NULL,_IONBF,0);
  27. printf("len: %d, len2: %d .", len, len2);
  28. }
911415deb596289ed1.png
864075deb596a953de.png
图1 test结构体内存示意图

char x1 占一个字节,位置如图中1。
Float x3 占四个字节,因此需要从4的整数倍开始,故char x1后面补三位,x3存在5-8字节处。
Short x2 占两个字节,8是2的整数倍,因此x2放在9-10字节处。
Char x4 占一个字节,因此放在11处。
整个结构体中,最长的是float ,占四个字节,因此使用sizeof()函数时,长度是4的整数倍,故char x4后面补一位,即图中12位置处。
Sizeof(test) 的返回值为12。


 楼主| dongnanxibei 发表于 2019-12-7 15:50 | 显示全部楼层
Test2结构体内存示意图如下:
850655deb5999b1c7e.png
图2 test2结构体内存占用示意图

char x1 占一个字节,位置如图2中1
Double x2 占8个字节,从8的整数倍开始,因此,x1后面补七个字节2-8,x2放在9-16字节处
Short x3 占两个字节,从2的整数倍开始,因此放在17-18字节处
Char x4 占一个字节,存放在19字节处
Short x5 占两个字节,从2的整数倍开始,因此x4后面补一个字节20,x5存放在21-22字节处
Test t 结构体,其数据元素最大占4个字节(float),从4的整数倍开始,因此x5后面补两个字节23-24
然后依次按规则存放test 结构体中的数据
Sizeof(test2)返回值为40

使用#pragma pack(push,1) #pragma pack(pop) 或 #pragma pack(1) #pragma pack(),使结构体按照自己的实际大小顺序存储。

除了1,还可以指定为2,4,8,16 。


 楼主| dongnanxibei 发表于 2019-12-7 15:50 | 显示全部楼层
四、补充 #pragma warning
示例: #pragma warning(disable:4786)

说明:该指令允许有选择性的修改编辑器的警告行为。

        指令格式如下:

#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...]
#pragma warning( push[ ,n ] )
#pragma warning( pop )

主要用到的警告表示有如下几个:

once:只显示一次(警告/错误等)消息
default:重置编译器的警告行为到默认状态
1,2,3,4:四个警告级别
disable:禁止指定的警告信息
error:将指定的警告信息作为错误报告

举例说明:

#pragma warning( disable : 4507 34; once : 4385; error : 164 )  
等价于:  
#pragma warning(disable:4507 34)  // 不显示4507和34号警告信息  
#pragma warning(once:4385)        // 4385号警告信息仅报告一次  
#pragma warning(error:164)        // 把164号警告信息作为一个错误。  
同时这个pragma warning 也支持如下格式:  
#pragma warning( push [ ,n ] )  
#pragma warning( pop )  
这里n代表一个警告等级(1---4)。  
#pragma warning( push )保存所有警告信息的现有的警告状态。  
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告  
等级设定为n。   
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的  
一切改动取消。例如:  
#pragma warning( push )  
#pragma warning( disable : 4705 )  
#pragma warning( disable : 4706 )  
#pragma warning( disable : 4707 )  
#pragma warning( pop )

在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)

在使用标准C++进行编程的时候经常会得到很多的警告信息,而这些警告信息都是不必要的提示,
所以我们可以使用#pragma warning(disable:4786)来禁止该类型的警告

在vc中使用ADO的时候也会得到不必要的警告信息,这个时候我们可以通过
#pragma warning(disable:4146)来消除该类型的警告信息
598330983 发表于 2019-12-7 20:55 | 显示全部楼层
没看懂,举例子没看到有那个预编译指令啊
643757107 发表于 2019-12-7 21:58 | 显示全部楼层
等我跑个例子试试看看添加与不添加的区别。
643757107 发表于 2019-12-7 22:03 | 显示全部楼层
果然给力,不用的话,那个test是12,用了就是8了,中间没有空隙了,牛B。
隔壁坏叔叔 发表于 2019-12-10 11:05 | 显示全部楼层
了解了。
antusheng 发表于 2019-12-11 21:18 | 显示全部楼层
原来如此,下次添加这个指令试试看。
antusheng 发表于 2019-12-11 21:19 | 显示全部楼层
上面高手讲的是
您需要登录后才可以回帖 登录 | 注册

本版积分规则

225

主题

3870

帖子

18

粉丝
快速回复 在线客服 返回列表 返回顶部