#pragma pack()用法详解

C++ #pragma pack()解析 我们知道计算机系统对基本数据类型在内存中的存放是有一定限制的,通常我们会要求这些数据的首地址的值是某个数的倍数(4或8),这就是所谓的内存对齐。       而每个平台上的编译器都有着自己的“默认对齐系数”(32位机一般为4,64位机一般为8),但我们可以通过预编译命令#pragma pack(k),k = 1,2,4,8,16来改变这个系数,其中的k就是对齐系数;当然也可以使用#pragma 阅读详情

以下内容转载自

https://blog.youkuaiyun.com/lime1991/article/details/44536343

1.什么是对齐?为什么要对齐?

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。


  
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n  )  

封装类是在内存中将其一个成员直接放在另一个后面,这可能表示对齐部分或全部成员的边界可以小于默认对齐目标体系结构。 pack 提供了数据声明级别的控制。 这与编译器选项 /Zp 不同,该选项只提供模块级别控制。 在杂注出现之后,pack 在第一个 struct、union 或 class 声明处生效。pack 对定义没有影响。 在没有参数的情况下调用 pack 会将 n 设置为编译器选项 /Zp 中设置的值。 如果未设置编译器选项,则默认值为 8。

如果更改某个结构的对齐方式,它可能不会使用像内存中一样多的空间,但您可能会发现性能降低或者甚至是因未对齐访问而遇到硬件产生的异常。 您可以使用 SetErrorMode 修改此异常行为。

show (可选)
显示封装对齐的当前字节值。 该值由警告消息显示。

push (可选)
将当前封装对齐值推送到内部编译器堆栈上,并将当前封装对齐值设置为 n。 如果未指定 n,则将推送当前封装对齐值。

pop (可选)
从内部编译器堆栈的顶部移除记录。 如果没有用 n 指定 pop,则与堆栈顶部的生成的记录关联的封装值是新的封装对齐值。 如果指定了 n(例如 #pragma pack(pop, 16)),n 将成为新的封装对齐值。 如果使用 identifier(例如 #pragma pack(pop, r1))进行弹出,则堆栈上的所有记录都将弹出,直到找到包含 identifier 的记录。 该记录将会弹出,与堆栈顶部的生成的记录关联的封装值是新的封装对齐值。 如果使用在堆栈上的任何记录中均未发现的 identifier 进行弹出,则会忽略 pop。

identifier (可选)
当与 push 一起使用时,为内部编译器堆栈上的记录指定名称。 当与 pop 一起使用时,从内部堆栈中弹出记录,直到移除 identifier;如果未在内部堆栈上找到 identifier,则不会弹出任何内容。

n(可选)
指定要用于封装的值(以字节为单位)。 如果没有为模块设置编译器选项 /Zp,n 的默认值为 8。 有效值为 1、2、4、8 和 16。 成员将在作为 n 的倍数或成员的大小的倍数的边界(以较小者为准)上对齐。

#pragma pack(pop, identifier``, n``) 是不确定的。

有关如何修改对齐方式的详细信息,请参阅以下主题:

  • __alignof

  • align

  • __unaligned

  • 结构对齐示例(特定于 x64)

    System_CAPS_ICON_warning.jpg 警告

    请注意,在 Visual Studio 2015 和更高版本可使用标准的 alignas 和 alignof 运算符,这 不同于 __alignof 和 declspec( align ),它们是跨编译器可移植的。 C++ 标准不能解决封装,因此仍必须使用 pack(或其他编译器上相应的扩展)来指定小于目标体系结构字体大小的对齐方式。

以下示例演示如何使用 pack 杂注更改结构的对齐方式。

// pragma_directives_pack.cpp  
#include <stddef.h>  
#include <stdio.h>  
  
struct S {  
   int i;   // size 4  
   short j;   // size 2  
   double k;   // size 8  
};  
  
#pragma pack(2)  
struct T {  
   int i;  
   short j;  
   double k;  
};  
  
int main() {  
   printf("%zu ", offsetof(S, i));  
   printf("%zu ", offsetof(S, j));  
   printf("%zu\n", offsetof(S, k));  
  
   printf("%zu ", offsetof(T, i));  
   printf("%zu ", offsetof(T, j));  
   printf("%zu\n", offsetof(T, k));  
}  

0 4 8  
0 4 6  

以下示例演示如何使用 push、pop 和 show 语法。

// pragma_directives_pack_2.cpp  
// compile with: /W1 /c  
#pragma pack()   // n defaults to 8; equivalent to /Zp8  
#pragma pack(show)   // C4810  
#pragma pack(4)   // n = 4  
#pragma pack(show)   // C4810  
#pragma pack(push, r1, 16)   // n = 16, pushed to stack  
#pragma pack(show)   // C4810  
#pragma pack(pop, r1, 2)   // n = 2 , stack popped  
#pragma pack(show)   // C4810  


3.1 基本数据类型所占内存大小




以下例子均按32bit编译器处理。

3.2 Test1

  1. #pragma pack(4)
  2. struct Test1
  3. {
  4. char c;
  5. short sh;
  6. int a;
  7. float f;
  8. int *p;
  9. char *s;
  10. double d;
  11. };

总共占28Bytes。 c的偏移量为0,占1个Byte。sh占2个Byte,它的对齐模数是2(2<4,取小者),存放起始地址应该是2的整数倍,因此c后填充1个空字符,sh的起始地址是2。a占4个Byte,对齐模数是4,因此接在sh后存放即可,偏移量为4。f占4个字节,对齐模数是4,存放地址是4的整数倍,起始地址是8。p,s的起始地址分别是12,16。d占8个字节,对齐模数是4(4<8),d从偏移地址为20处存放。存放后结构体占28个字节,是4的整数倍不用补空字符。



  1. struct Test2
  2. {
  3. char c;
  4. double d;
  5. int a;
  6. short sh;
  7. float f;
  8. int *p;
  9. char *s;
  10. };

将Test1个变量的顺序换一下位置,结构体Test2占用内存32Byte,可见写结构体时,将各个变量按所占内存从小到大排列所占结构体所占内存较小。



 

3.3关于静态变量static

静态变量的存放位置与结构体实例的存储地址无关,是单独存放在静态数据区的,因此用siezof计算其大小时没有将静态成员所占的空间计算进来。

  1. #pragma pack(4)
  2. struct Test3
  3. {
  4. char c;
  5. short sh;
  6. int a;
  7. float f;
  8. int *p;
  9. char *s;
  10. double d;
  11. static double sb;
  12. static int sn;
  13. };

sizeof(Test3)=28

3.4关于类

空类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。

(一)类内部的成员变量:

  • 普通的变量:是要占用内存的,但是要注意对齐原则(这点和struct类型很相似)。
  • static修饰的静态变量:不占用内容,原因是编译器将其放在全局变量区。

(二)类内部的成员函数:

  • 普通函数:不占用内存。
  • 虚函数:要占用4个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的

  1. #pragma pack(4)
  2. class cBase{};

sizeof(cBase)=1

3.4.1 不包含虚函数的类

  1. #pragma pack(4)
  2. class CBase1
  3. {
  4. private:
  5. char c;
  6. short sh;
  7. int a;
  8. public:
  9. void fOut(){ cout << "hello" << endl; }
  10. };
不包含虚函数时,对于类中的成员变量按结构体对齐方式处理,普通函数函数不占内存。sizeof(CBase1)=8

3.4.2 包含虚函数的类

  1. #pragma pack(4)
  2. class CBase2
  3. {
  4. private:
  5. char c;
  6. short sh;
  7. int a;
  8. public:
  9. virtual void fOut(){ cout << "hello" << endl; }
  10. };
包含虚函数时,类中需要保存虚函数表的入口地址指针,即需要多保存一个指针。这个值跟虚函数的个数多少没有关系。sizeof(CBase2)=12

3.4.3 子类

子类所占内存大小是父类+自身成员变量的值。特别注意的是,子类与父类共享同一个虚函数指针,因此当子类新声明一个虚函数时,不必在对其保存虚函数表指针入口。
  1. #pragma pack(4)
  2. class CBase2
  3. {
  4. private:
  5. char c;
  6. short sh;
  7. int a;
  8. public:
  9. virtual void fOut(){ cout << "virtual 1" << endl; }
  10. };
  11. class cDerive : public CBase
  12. {
  13. private :
  14. int n;
  15. public:
  16. virtual void fPut(){ cout << "virtual 2"; }
  17. };

sizeof(cDerive)= sizeof(cBase)+sizeof(int n) = 16



#pragma pack()用法--C++面试细节(1) #pragma pack(n)的意思是告诉编译器字节对齐方式为n字节对齐,n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须 阅读详情

相关推荐

生成式引擎优化(GEO)的概念与技术路径

生成式引擎优化(GEO)是针对生成式AI引擎的新型优化策略,旨在提升内容在AI搜索与问答平台中的可见性和可信度。与传统SEO相比,GEO更注重语义理解、知识图谱和多模态生成等技术,通过优化内容的结构化和平台适配性,使品牌信息在生成式AI中更易被采纳和呈现。其核心技术包括语义理解、向量数据库和实时反馈优化等,应用于品牌传播、智能客服和个性化推荐等领域。随着AI技术发展,GEO将向智能化、自动化方向演进,成为内容优化的重要工具。

Baixue5209的博客 1047

pragma pack() 详解

1.#pragma简述 预处理指令,作用是设定编译器的状态或者指示编译器完成一些特定动作。 #pragma指令对每个编译器给出一种方法,保持与C和C++的完全兼容的情况下,给出主机和操作系统专有的特征。所以每个编译器都是不同的。 2.#pragma pack作用 主要作用就是改变编译器的内存对齐方式,在网络报文的处理有重要的作用。基本用法:#pragma pack(n) ,不使用这条指令,采取默认的字节对齐方式(#pragma pack(8)) n可以取(1, 2, 4, 8, 1...

m0_51407835的博客 1万+

从Chiplet到UCIE:D2D互联技术如何重塑半导体设计生态

本文探讨了D2D互联技术在半导体设计中的革命性作用,从Chiplet到UCIE标准的演进如何重塑行业生态。详细分析了D2D互联的技术本质、架构选择及UCIE标准带来的变革,并展望了未来光学D2D和3D集成等趋势。高速互联接口技术的突破为AI加速器和计算芯片提供了更高带宽和能效。

weixin_29292535的博客 931

#pragma pack详解

数据对齐 程序中变量的存储可以直接影响到程序的运行速度。计算机中都是以字节划分内存空间,通常编译器会为我们选择适合目标平台的对齐策略,但是有时候也带了一些麻烦,要自定义变量的对齐策略我们就需要用到#pragma pack。 应用场景 百度百科里提到一个典型的应用场景:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,

oushuangjiang的博客 7449

Cadence Allegro 17.4学习记录开始00

Cadence Allegro 有三个版本,16.6,17.2,17.4。现在主要学习最新的版本17.4.原理图设计:OrCADPCB设计:Allegro主要用到的就下面三个图片软件:PCB Editor 17.4 是用来画PCB和元器件封装的;Padstack Editor 17.4 是用来画元器件焊盘的;Capture CIS 17.4 是用来画原理图和原理图库的。其实Cadence还有很多功能,现在计划先学习以上内容,后面熟悉了这个软件,还可以继续学习放仿真等功能。

qq_31444421的博客 7441

电磁阀颤振工作原理

颤振 颤振,弹性结构在均匀气(或液)流中受到空气(或液体)动力、弹性力和惯性力(见达朗伯原理)的耦合作用而发生的大幅度振动,它是气动弹性力学中最重要的问题之一。 随着机电液一体化技术的发展,电液比例控制技术的应用越来越广泛。电液比例控制系统范围较广,在液压传动与控制过程中,系统能够接受模拟信号或数字信号,使输出的流量或压力连续成比例地变化,都属于此类。该控制系统的优点是充分利用了液压控制与电气控制的长处,即功率传递靠液压,信号处理靠电气的优化措施。 电液比例控制原理与要求 液压控制系统中两个最重要的被控参数

小幽余生不加糖 1万+

#pragma pack()详解

#pragma pack()详解,并对其进行举例说明

weixin_51361507的博客 2094

基于树莓派4B设计的厨房环境监测系统_368

本项目设计并实现了一种基于树莓派4B的厨房环境监测系统,提升厨房使用过程中的安全性与智能化水平。系统采用树莓派4B作为主控核心,集成DHT11温湿度传感器、MQ2烟雾传感器、火焰传感器、光敏传感器、水滴检测传感器等多种传感器,实现对厨房环境温湿度、烟雾、火焰、光照与溢水等参数的实时监测,并通过蜂鸣器进行本地报警。系统通过WIFI连接互联网,利用MQTT协议将环境数据上传至华为云物联网平台,实现远程可视化监控。

415

理解C++编译指令#pragma pack(1)

内存对齐是一种优化内存访问速度的技术,它会根据数据类型的大小来调整数据在内存中的位置,使得数据的起始地址是它的大小的整数倍。例如,如果一个int类型的数据占4个字节,那么它的起始地址应该是4的整数倍,比如0,4,8,12等。如果一个char类型的数据占1个字节,那么它的起始地址可以是任意的,比如0,1,2,3等。

fasdfasfwerwqf的博客 8507

ubuntu 20.04 搭建多用户远程桌面共享及同时在线使用

摘要:本文介绍了在物理机上配置远程桌面访问的完整流程。通过setup_xrdp.sh脚本自动安装xrdp服务,修改配置文件并开放3389端口。同时提供了用户管理脚本:add_rdp_users.sh用于创建带sudo权限的远程用户,del_rdp_users.sh用于删除用户及主目录。还包含批量操作脚本add_rdp_all.sh和del_rdp_all.sh,支持一次性创建/删除多个用户(如dev1-dev5),所有用户密码统一设置为111111。完整方案实现了远程桌面环境的快速部署和用户集中管理。

622

爆破shadow文件密码脚本(完成版)

由于添加了一个for循环,再稍作修改,让password not found!print(f"Trying password:")显示爆破的密码和过程。将代码中的password="123456"替换成下面代码。line.strip()函数首尾去空格。

m0_56578216的博客 771

#pragma pack 用法详解

#pragma pack 用法详解 pack为struct、union和class等的成员对齐指定字节边界,与编译选项(属性 -&gt; 配置属性 -&gt; C/C++ -&gt;代码生成 -&gt; 结构成员对齐)的/Zp开关不同,它不针对整个项目,而仅针对模块,比如一个编译单元。 1. #pragma pack(show) 以警告信息的形式显示当前字节对齐的值。 2. #pragma...

《好好先生》专栏 6859

PyTorch生成式人工智能(1)——神经网络与模型训练过程详解

在本节中,我们介绍了传统机器学习与人工神经网络间的差异,并了解了如何在实现前向传播之前连接网络的各个层,以计算与网络当前权重对应的损失值;实现了反向传播以优化权重达到最小化损失值的目标。并实现了网络的所有关键组成——前向传播、激活函数、损失函数、链式法则和梯度下降,从零开始构建并训练了一个简单的神经网络。

盼小辉丶的博客 1万+

项目中#pragma pack()错误用法及使用详解

#pragma pack()

W_ide的博客 3804

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

详解C/C++中的的:#pragma pack(push) 、#pragma pack(pop) 和#pragma pack()

前言 我们知道结构体内存对齐字节可以通过#pragma pack(n) 的方式来指定。 但是,有没有想过一个问题,某些时候我想4字节对齐,有些时候我又想1字节或者8字节对齐,那么怎么解决这个问题呢? 此时,#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()应运而生。 看测试代码:(说明,64位GCC,默认8字节对齐) 屏蔽了的代...

WindSun's Blog 3626

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

结构体内存对齐 和 #pragma pack()

为了保证计算机读取数据的高效性,编译器通常在结构体的成员数据进行内存对齐,使每一个成员变量都对齐在自然边界上,同时也有一定的空间牺牲。 我们重点考虑成员变量是如何对齐的: 文章目录对齐规则(偏移规则)#pragma pack()改变默认对齐实例理解结构体嵌套时对齐 对齐规则(偏移规则) 每个成员按照其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中的较小的一个对齐,即min(n,sizeo...

博客小站 2957

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

关于之前对#pragma pack ()误解的纠正

如题

No detail can be too detailed 786

#pragma pack() 易忽略的问题

#pragma pack() 易忽略的问题   C/C++中,class、struct、union默认的大小对齐方式为按照成员变量所占空间最大的大小进行对齐,如: typedef struct test{ char a; int b; double c; }test_t; 则test结构体内所有成员变量按照double类型进行字节对齐,此时sizeof(test_t)等于8*3。在网络通信中,为了让数据包更紧凑些,我们往往希望能够通过#pragma pack预编译命令改变默

modi000的博客 939

C语言中 #pragma pack()

#pragma pack 这条指令主要用作改变编译器的默认对齐方式。 pragma pack(show) //显示当前内存对齐的字节数,编辑器默认8字节对齐 #pragma pack(n) //设置编辑器按照n个字节对齐,n可以取值1,2,4,8,16 #pragma pack(push) //将当前的对齐字节...

坚持 1万+

关于结构体内存对齐方式的总结(#pragma pack()和alignas())

最近闲来无事,翻阅msdn,在预编译指令中,翻阅到#pragma pack这个预处理指令,这个预处理指令为结构体内存对齐指令,偶然发现还有另外的内存对齐指令aligns(C++11),__declspec(align(#))(Microsoft专用),遂去探究两者之间的不同点。1、#pragma pack这个指令为预处理指令,所谓与处理指令执行在程序的预处理阶段,该指令对应着编译选项/Zp,可以在...

老院长的博客 3235

0038__#pragma pack()用法详解

https://www.cnblogs.com/flyinggod/p/8343478.html

行潇 154

【C语言】#pragma pack(1) 和#pragma pack()

#pragma pack(1) 设置结构体的边界对齐为1个字节,也就是所有数据在内存中是连续存储的。

Roger_717的博客 5544

#pragma pack()

是一种编译器指令,用于控制数据结构在内存中的对齐方式。它可以改变数据结构的对齐要求,以优化内存使用或与特定的硬件或协议规范对齐。虽然是 C++ 标准中不定义的,但它在许多编译器中得到了支持(如 GCC、MSVC 和 Clang)。使用和可以设置和恢复对齐方式。然而,使用 #pragma pack() 时需要考虑可移植性、性能以及硬件对齐要求。

m0_60697583的博客 1051

#pragma pack()用法

在阅读代码特别是C语法代码的过程中,其实经常会遇到#pragma pack(n),#pragma pack(),#pragma pack(pack,n),#pragma pack(pop,n)等,其中n是可以省略的。 并且这些语句一般出现在结构体前面。我们一般都知道该语句是用来进行内存对齐的,但是总结得不是特别清晰。 1.#pragma简述: 在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个

qq_34272143的博客 2797

#pragma pack() 释疑

1.#pragma简述 在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma 指令对每个编译器给出了一个方法,在保持与 C 和 C++ 语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 2.#pragma pack的作用 #pragma pack 的主要作用就是改变编译器的内存对齐方式,这个指令在网络报文的处理中有着重要的作用,#pragma pac

bigchen 4107
上一篇: #pragma pack(push,1)与#pragma pack(1)
下一篇: 漫画:经典谷歌面试题“扔鸡蛋”,看看你会做吗?
shankusu2017
博客等级 码龄15年 155粉丝 155原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值