#Pragma Pack(n)与内存分配 pragma pack(push,1)与#pragma pack(1)的区别

from:http://blog.csdn.net/mylinx/article/details/7007309


 #pragma pack(n)

解释一:

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

  规则:

  1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

  2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

 

解释二:

n 字节的对齐方式 VC 对结构的存储的特殊处理确实提高 CPU 存储变量的速度,但是有时候也带来 了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。 VC 中提供了#pragma pack(n)来设定变量以 n 字节对齐方式。n 字节对齐就是说 变量存放的起始地址的偏移量有两种情况:

第一、如果 n 大于等于该变量所占用的字 节数,那么偏移量必须满足默认的对齐方式。

第二、如果 n 小于该变量的类型所占用 的字节数,那么偏移量为 n 的倍数,不用满足默认的对齐方式。结构的总大小也有个 约束条件,分下面两种情况:如果 n 大于所有成员变量类型所占用的字节数,那么结 构的总大小必须为占用空间最大的变量占用的空间数的倍数; 否则必须为 n 的倍数。

下面举例说明其用法。 #pragma pack(push) //保存对齐状态

 #pragma pack(4)//设定为 4 字节对齐

struct test { char m1; double m4; int m3; }; #pragma pack(pop)//恢复对齐状态 以上结构体的大小为 16:

下面分析其存储情况,首先为 m1 分配空间,其偏移量 为 0,满足我们自己设定的对齐方式(4 字节对齐),m1 大小为 1 个字节。接着开始 为 m4 分配空间,这时其偏移量为 1,需要补足 3 个字节,这样使偏移量满足为 n=4 的倍数(因为 sizeof(double)大于 4),m4 占用 8 个字节。接着为 m3 分配空间,这时 其偏移量为 12,满足为 4 的倍数,m3 占用 4 个字节。这时已经为所有成员变量分配 了空间,共分配了 16 个字节,满足为 n 的倍数。如果把上面的#pragma pack(4)改为 #pragma pack(8),那么我们可以得到结构的大小为 24。

 

大家看了这些文字描述头也一定会发麻吧,我坚持读完后,然后自己编写了一个程序:

#pragma pack(4)

struct node{

  int e;
  char f;
  short int a;
  char b;

};

struct node n;

printf("%d\n",sizeof(n));

我自己算的结果是16,结果实际结果是:

 

然后结构体内部数据成员变动一下位置:

 

#pragma pack(4)

struct node{

  char f;
  int e;
  short int a;
  char b;};

struct node n;

printf("%d\n",sizeof(n));

 

将对齐位数强制定位2

#pragma pack(2)

struct node{

  char f;
  int e;
  short int a;
  char b;};

struct node n;

printf("%d\n",sizeof(n));

 

将对齐位数强制定位1

#pragma pack(1)

struct node{

  char f;
  int e;
  short int a;
  char b;};

struct node n;

printf("%d\n",sizeof(n));

 

看着输出结果和文字描述有点晕,下面简单说一下俺的判定规则吧:

 

其实之所以有内存字节对齐机制,就是为了最大限度的减少内存读取次数。我们知道CPU读取速度比内存读取速度快至少一个数量级,所以为了节省运算花费时间,只能以牺牲空间来换取时间了。

下面举例说明如何最大限度的减少读取次数。

#pragma pack(1)

struct node{

  char f;
  int e;
  short int a;
  char b;};

struct node n;

printf("%d\n",sizeof(n));

这里强制按照1字节进行对齐,可以理解成所有的内容都是按照1字节进行读取(暂且这样理解,因为这样可以很好的理解内存对其机制),其他所有的数据成员都是1字节的整数倍,所以也就不用进行内存对其,各个成员在内存中就按照实际顺序进行排列,结构体实际长度为8

 

 

#pragma pack(2)

struct node{

  char f;
  int e;
  short int a;
  char b;};

struct node n;

printf("%d\n",sizeof(n));

这里强制按照2字节进行对齐。如果内存分布仍然是连续的话,那么int e就得三次才能读到CPU中,所以为了“讲究”int e的读取,所以在char f之后预留1BYTE,最后的char b也是如此,所以长度为10

 

#pragma pack(4)

struct node{

  char f;
  int e;
  short int a;
  char b;};

struct node n;

printf("%d\n",sizeof(n));

这里强制按照4字节进行对齐。所以char f后要预留3BYTE,而short int a 和 char b可以一次读取到CPU(按照4字节读取),所以长度为12

 

如果#pramga pack(n)中的n大于结构体成员中任何一个成员所占用的字节数,则该n值无效。编译器会选取结构体中最大数据成员的字节数为基准进行对其

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

#pragma pack(push,1)与#pragma pack(1)的区别


这是给编译器用的参数设置,有关结构体字节对齐方式设置, #pragma pack是指定数据在内存中的对齐方式。

#pragma pack (n)             作用:C编译器将按照n个字节对齐。
#pragma pack ()               作用:取消自定义字节对齐方式。


#pragma  pack (push,1)     作用:是指把原来对齐方式设置压栈,并设新的对齐方式设置为1个字节对齐

#pragma pack(pop)            作用:恢复对齐状态

因此可见,加入push和pop可以使对齐恢复到原来状态,而不是编译器默认,可以说后者更优,但是很多时候两者差别不大

如:

#pragma pack(push) //保存当前对齐状态

#pragma pack(4)//设定为4字节对齐

  相当于 #pragma  pack (push,4)  

 

#pragma  pack (1)           作用:调整结构体的边界对齐,让其以一个字节对齐;<使结构体按1字节方式对齐>

#pragma  pack ()

例如:

#pragma pack(1)

struct sample
{
char a;
double b;
};

#pragma pack()

注:若不用#pragma pack(1)和#pragma pack()括起来,则sample按编译器默认方式对齐(成员中size最大的那个)。即按8字节(double)对齐,则sizeof(sample)==16.成员char a占了8个字节(其中7个是空字节);若用#pragma pack(1),则sample按1字节方式对齐sizeof(sample)==9.(无空字节),比较节省空间啦,有些场和还可使结构体更易于控制。

应用实例

在网络协议编程中,经常会处理不同协议的数据报文。一种方法是通过指针偏移的方法来得到各种信息,但这样做不仅编程复杂,而且一旦协议有变化,程序修改起来也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做,不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,其它程序无需修改,省时省力。下面以TCP协议首部为例,说明如何定义协议结构。其协议结构定义如下: 

#pragma pack(1) // 按照1字节方式进行对齐
struct TCPHEADER 
{
     short SrcPort; 
// 16位源端口号
     short DstPort; 
// 16位目的端口号
     int SerialNo; 
// 32位序列号
     int AckNo; 
// 32位确认号
     unsigned char HaderLen : 4; 
// 4位首部长度
     unsigned char Reserved1 : 4; 
// 保留6位中的4位
     unsigned char Reserved2 : 2; 
// 保留6位中的2位
     unsigned char URG : 1;
     unsigned char ACK : 1;
     unsigned char PSH : 1;
     unsigned char RST : 1;
     unsigned char SYN : 1;
     unsigned char FIN : 1;
     short WindowSize; 
// 16位窗口大小
     short TcpChkSum; 
// 16位TCP检验和
     short UrgentPointer; 
// 16位紧急指针
}; 
#pragma pack()


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458512.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ShowDoc的搭建

其实&#xff0c;官方文档也说的很清楚了。主要贴一下我遇见的问题。环境&#xff1a;LNMP&#xff08;LAMP没试过&#xff0c;有兴趣的同学可以试试&#xff0c;然后发出来&#xff09;PHP5.3以上版本、php-mbstring模块、php-pdo模块、mysql数据库克隆或者下载代码&#xff1…

BroadcastReceiver

本文介绍Broadcast Receiver&#xff0c;包括几部分内容&#xff1a;Broadcast Receiver概述及实例、自定义Broadcast Receiver、Broadcast Receiver的实现细节、生命周期等。 csdn貌似今天出问题了&#xff0c;无法上传图片。 资料来源&#xff1a;最牛网&#xff0c;《官方解…

如何确定VS编译器版本--_MSC_VER || #if _MSC_VER 1000 #pragma once #endif

如何确定VS编译器版本 _MSC_VER是MSVC编译器的内置宏&#xff0c;定义了编译器的版本&#xff0c;_MSC_VER 值对应版本关系 MSVC 11.0 _MSC_VER 1700 (Visual Studio 2012) MSVC 10.0 _MSC_VER 1600 (Visual Studio 2010) MSVC 9.0 _MSC_VER 1500 (Visual Studio 2008) …

NIO复习02

Selector 1. Selector&#xff08;选择器&#xff09;是Java NIO中能够检测一到多个NIO通道&#xff0c;并能够知晓通道是否为诸如读写事件做好准备的组件。这样&#xff0c;一个单独的线程可以管理多个channel&#xff0c;从而管理多个网络连接。 2. Selector的创建&#xff1…

c/c++标准预定义宏

C标准中的一些预定义宏昨天写代码时需要在代码获取当前编译时间&#xff0c;从而可动态地作为版本信息&#xff0c;因此用到了C标准中的一些预定义的宏。在此将C标准中定义的几个宏一并总结一下&#xff1a; __DATE__ 进行预处理的日期&#xff08;“Mmm dd yyyy”形式的字符串…

安装cocoa pods时出现Operation not permitted - /usr/bin/xcodeproj的问题

在更新完ruby&#xff0c;更换Ruby镜像&#xff0d;&#xff0d;淘宝的那个镜像(https://ruby.taobao.org/ )已经不可用了。官方的需要FQ。所以我们现在用最新支持的ruby镜像(https://gems.ruby-china.org/) 安装cocoa pods时&#xff0c; 在命令行中输入&#xff1a; 安装&…

js 日期控件laydate使用

官网 http://sentsin.com/layui/laydate/ 1. 下载官网上的压缩包,解压后只需要复制laydate 文件夹到你的项目中; 2. 在页面引入 <script type"text/javascript" src"js/laydate/laydate.js"></script> 3. 在页面添加: <div class"i…

老李推荐:第8章2节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-解析处理命令行参数 2...

我们这一节会先去分析下monkeyrunner是如何对参数进行处理的&#xff0c;我们跳转到MonkeyRunnerOptions这个类里面的processOptions这个方法&#xff1a; 93 public static MonkeyRunnerOptions processOptions(String[] args) 94 { 95 int index 0; 96 97 Strin…

MFC CPropertySheet 多页面切换 实例

为了能实现在同一个页面实现多个页面的切换效果。CPropertySheet要与CPropertyPage一起使用。 首先 新建一个MFC工程--MFC AppWizard(exe), 取名Pagesheet, 选择Dialog based, 然后Finish. 2. 在对话框资源中插入两个对话框IDD_DIALOG1、IDD_DIALOG2&#xff0c;作为…

ReactiveCocoa简单介绍

ReactiveCocoa 的简单介绍 http://www.jianshu.com/p/87ef6720a096

游戏提高性能 游戏降帧处理

什么是降帧&为什么降帧 一般情况下我们为了提高整个游戏的体验&#xff0c;所以我们一般会将游戏的帧数&#xff08;每秒钟刷新多少次&#xff09;设置的比较高。一般情况下&#xff0c;我们的游戏所有的代码都是一帧执行一次。为了让每一帧都变成真的关键数据帧。不过&…

delphi 更改DBGrid 颜色技巧

1、根据条件更改某一单元格的颜色 [delphi] view plaincopy procedure TMainFrm.First_DGDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin if DM.FirstListStatus.AsString生产 then begin …

mfc中UpdateData的用法

http://blog.csdn.net/ddjj_1980/article/details/51452289 UpdateData(TRUE)——刷新控件的值到对应的变量。(外部输入值交给内部变量)即&#xff1a;控件的值—>变量。UpdateData(FALSE)—— 拷贝变量值到控件显示。(变量的最终运算结果值交给外部输出显示)即&#xff1a;…

动画类的层次结构

CASpringAnimation类是CAABasicAnimation的子类 CAPropertyAnimation &#xff1a;是CAAnimation的子类&#xff0c;它支持动画地显示图层的keyPath&#xff0c;一般不直接使用。 iOS9.0之后新增CASpringAnimation类&#xff0c;它实现弹簧效果的动画&#xff0c;是CABasicAnim…

[原]小命令大作用:modprobe

调整网络为绑定模式&#xff0c;但启动时候会报错&#xff0c;此时执行命令 modprobe bonding 可以解决问题。 之前在磁盘方面遇到问题也是通过modprobe命令解决的&#xff0c;又遇到这个命令&#xff0c;似曾相识。于是看下该命令&#xff1a; Linux命令&#xff1a;modprobe …

sql语句优化之not in

多表关联想查a表中除去b表的可用not exists 效率比not in 更高 优化后的语句用时0.421秒 select john.*, (case when round((case john.sumcount when 0 then 0 else mycount * 100 / sumcount end), 2) > 100 then 100 else round((case john.sumcount when 0 then 0 else …

setup2go制作安装程序

QT程序设计完毕时&#xff0c;我们就要发布自己的程序&#xff0c;发布程序有两种方法&#xff1a;一是静态编译&#xff0c;二是制作程序安装包。 静态编译好麻烦&#xff0c;我从来没有成功过&#xff0c;所以我用了动态编译方法&#xff0c;设计完成时&#xff0c;用release…

gson-2.2.api简单

使用gson的tojson和fromjson实现对象和json的转换 Gson gson new Gson(); // Or use new GsonBuilder().create(); MyType target new MyType(); String json gson.toJson(target); // serializes target to Json MyType target2 gson.fromJson(json, MyType.c…

用户需求、己、竞争对手的关系

图一 图二 图三 如第一图所示&#xff1a;现在在市面上的产品基本上都可以归纳为这一方面&#xff0c; 如“用户需求”是网上购物的需求&#xff0c;以“己”是京东为例子&#xff0c;“己”的…