打印
[牛人杂谈]

C #pragma pack(push,1) #pragma pack(pop)解析

[复制链接]
821|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、结构
#ifdef _WIN32
#pragma pack( push, 1 )
#else
#pragma pack(1)
#endif

Typedef  struct

{

}

#ifdef _WIN32
#pargma pack(pop)
#else
#pragma pack()
#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 | 只看该作者
三、示例
#include<stdio.h>

struct test
{
char x1;
float x3;
short x2;
char x4;
};

struct test2
{
char x1;
double x2;
short x3;
char x4;
short x5;
struct test t;
};
void main(void)
{
struct test t;
struct test2 t2;
int len;
int len2;
len=sizeof(t);
len2=sizeof(t2);
setvbuf(stdout,NULL,_IONBF,0);
printf("len: %d, len2: %d .", len, len2);
}


图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结构体内存示意图如下:

图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)来消除该类型的警告信息

使用特权

评论回复
5
598330983| | 2019-12-7 20:55 | 只看该作者
没看懂,举例子没看到有那个预编译指令啊

使用特权

评论回复
6
643757107| | 2019-12-7 21:58 | 只看该作者
等我跑个例子试试看看添加与不添加的区别。

使用特权

评论回复
7
643757107| | 2019-12-7 22:03 | 只看该作者
果然给力,不用的话,那个test是12,用了就是8了,中间没有空隙了,牛B。

使用特权

评论回复
8
隔壁坏叔叔| | 2019-12-10 11:05 | 只看该作者
了解了。

使用特权

评论回复
9
antusheng| | 2019-12-11 21:18 | 只看该作者
原来如此,下次添加这个指令试试看。

使用特权

评论回复
10
antusheng| | 2019-12-11 21:19 | 只看该作者
上面高手讲的是

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

198

主题

3537

帖子

16

粉丝