前言

我们知道结构体内存对齐字节可以通过#pragma pack(n) 的方式来指定。

但是,有没有想过一个问题,某些时候我想4字节对齐,有些时候我又想1字节或者8字节对齐,那么怎么解决这个问题呢?

此时,#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()应运而生。

看测试代码:(说明,64位GCC,默认8字节对齐)



屏蔽了的代码先别看,只看这个结构体,在默认8字节对齐的方式下,sizeof大小为24个字节,这不再做分析,之前随笔分析过了。

pragma pack()

然后我加上强制4字节对齐之后:



那么现在,我再新建一个结构体B,内容和结构体C一样,只是对齐方式向分别采取不同的方式:

#include <stdio.h>

#pragma   pack(4)
struct C {
double d;
char b;
int a;
short c;
};
#pragma pack()
struct B {
double d;
char b;
int a;
short c;
};

像上面那样处理之后,输出:先打印结构C,再打印结构B



这说明了,在强制4字节对齐之后,我加上#pragma pack() ,能够让程序恢复默认对齐(这里是8字节)状态。

#pragma pack() 能够取消自定义的对齐方式,恢复默认对齐。

pragma(pop) & pragma pack(push)

继续测试:

#pragma   pack(4)
struct CC {
double d;
char b;
int a;
short c;
};
#pragma pack(pop)
struct BB{
double d;
char b;
int a;
short c;
};

输出:



好像没什么作用的感觉,那么再加上一个#pragma pack(push)试试呢?

#include <stdio.h>

#pragma pack(push)
#pragma pack(4) struct CC {
double d;
char b;
int a;
short c;
}; #pragma pack(pop) struct BB{
double d;
char b;
int a;
short c;
};
int main(void)
{ printf("%u\n%u\n",sizeof(struct CC),sizeof(struct BB));
return 0;
}



这样似乎改变了,有不同的地方体现了出来。

#pragma pack(push):

英文单词push是“压入”的意思。编译器编译到此处时将保存对齐状态(保存的是push指令之前的对齐状态)。

#pragma pack(pop):

英文单词pop是”弹出“的意思。编译器编译到此处时将恢复push指令前保存的对齐状态(请在使用该预处理命令之前使用#pragma pack(push))。

push和pop是一对应该同时出现的名词,只有pop没有push不起作用,只有push没有pop可以保持之前对齐状态(但是这样就没有使用push的必要了)。

这样就可以知道,当我们想要一个结构体按照4字节对齐时,可以使用#pragma pack(4) ,最后又想使用默认对齐方式时,可以使用#pragma pack() ;

也可以使用:

#pragma pack(push)
#pragma pack(4)
struct...
#pragma pack(pop)

这样在push和pop之间的结构体就可以按照pack指定的字节(这里是4字节对齐方式),而pop之后的结构体按照#pragma pack(push) 前对齐方式。

eg:

#include <stdio.h>
#pragma pack(2)
#pragma pack(push)
#pragma pack(4)
struct CC {
double d;
char b;
int a;
short c;
}; #pragma pack(1)
struct BB{
double d;
char b;
int a;
short c;
};
#pragma pack(pop)
struct AA{
double d;
char b;
int a;
short c;
};
int main(void)
{ printf("%u\n%u\n%u\n",sizeof(struct CC),sizeof(struct BB),sizeof(struct AA));
return 0;
}



先按照2字节对齐,然后push保存2字节对齐,然后又强制4字节对齐,打印CC为20字节,然后强制1字节对齐,打印BB为15字节,然后pop,pop会让编译器回到push之前的对齐方式(这里是2字节对齐),打印AA(按照2字节对齐)16字节。

注意,#pragma pack() 取消自定义对齐方式,恢复默认方式,而push之后pop是回到push指令之前的对齐方式。

eg:

#include <stdio.h>
#pragma pack(2)
#pragma pack(push)
#pragma pack(4)
struct CC {
double d;
char b;
int a;
short c;
}; #pragma pack(1)
struct BB{
double d;
char b;
int a;
short c;
};
#pragma pack()
struct AA{
double d;
char b;
int a;
short c;
};
int main(void)
{ printf("%u\n%u\n%u\n",sizeof(struct CC),sizeof(struct BB),sizeof(struct AA));
return 0;
}

只把pop改成pack()打印如下:



最后回到的不是2字节对齐,而是默认的8字节对齐。

延伸

还有延伸点:



上图红色处等价于它下面的两句。

语法:

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

说明:

1,pack提供数据声明级别的控制,对定义不起作用;

2,调用pack时不指定参数,n将被设成默认值;

3,一旦改变数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降;

语法具体分析:

1,show:可选参数;显示当前packing aligment的字节数,以warning message的形式被显示;

2,push:可选参数;将当前指定的packing alignment数值进行压栈操作,这里的栈是the internal compiler stack,同时设置当前的packing alignment为n;如果n没有指定,则将当前的packing alignment数值压栈;

3,pop:可选参数;从internal compiler stack中删除最顶端的record;如果没有指定n,则当前栈顶record即为新的packing alignment数值;如果指定了n,则n将成为新的packing aligment数值;如果指定了identifier,则internal compiler stack中的record都将被pop直到identifier被找到,然后pop出identitier,同时设置packing alignment数值为当前栈顶的record;如果指定的identifier并不存在于internal compiler stack,则pop操作被忽略;

4,identifier:可选参数;当同push一起使用时,赋予当前被压入栈中的record一个名称;当同pop一起使用时,从internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作;

5,n:可选参数;指定packing的数值,以字节为单位;

另外:

__attribute(aligned(n)),让所作用的数据成员对齐在n字节的自然边界上;如果结构中有成员的长度大于n,则按照最大成员的长度来对齐;

__attribute((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐

参考

参考原文:https://www.cnblogs.com/yangguang-it/p/7392726.html

详解C/C++中的的:#pragma pack(push) 、#pragma pack(pop) 和#pragma pack()的更多相关文章

  1. 详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别

    详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别 http://blog.sina.com.cn/s/blog_686999de0100jgda.html   实例: ...

  2. 详解 Go 语言中的 time.Duration 类型

    swardsman详解 Go 语言中的 time.Duration 类型swardsman · 2018-03-17 23:10:54 · 5448 次点击 · 预计阅读时间 5 分钟 · 31分钟之 ...

  3. 详解jquery插件中(function ( $, window, document, undefined )的作用。

    1.(function(window,undefined){})(window); Q:(function(window,undefined){})(window);中为什么要将window和unde ...

  4. zz详解深度学习中的Normalization,BN/LN/WN

    详解深度学习中的Normalization,BN/LN/WN 讲得是相当之透彻清晰了 深度神经网络模型训练之难众所周知,其中一个重要的现象就是 Internal Covariate Shift. Ba ...

  5. [转载]详解网络传输中的三张表,MAC地址表、ARP缓存表以及路由表

    [转载]详解网络传输中的三张表,MAC地址表.ARP缓存表以及路由表 虽然学过了计算机网络,但是这部分还是有点乱.正好在网上看到了一篇文章,讲的很透彻,转载过来康康. 本文出自 "邓奇的Bl ...

  6. 详解WebService开发中四个常见问题(2)

    详解WebService开发中四个常见问题(2)   WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...

  7. 详解WebService开发中四个常见问题(1)

    详解WebService开发中四个常见问题(1)   WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...

  8. 详解Python编程中基本的数学计算使用

    详解Python编程中基本的数学计算使用 在Python中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯 ...

  9. 第7.16节 案例详解:Python中classmethod定义的类方法

    第7.16节  案例详解:Python中classmethod定义的类方法 上节介绍了类方法定义的语法以及各种使用的场景,本节结合上节的知识具体举例说明相关内容. 一.    案例说明 本节定义的一个 ...

随机推荐

  1. Docker下Jenkins的安装部署、更新

    一.下载Jenkins镜像 docker pull jenkins/jenkins 二.创建挂载文件 mkdir /srv/jenkins chown -R : /srv/jenkins 三.启动Do ...

  2. .NET CORE(C#) WPF 值得推荐的动画菜单设计

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. .NET CORE(C#) WPF 值得推荐的动画菜单设计 阅读导航 本文背景 代码实现 本文 ...

  3. Python之一、#!/usr/bin/python到底是什么意思

    引用https://www.cnblogs.com/furuihua/p/11213486.html 关于脚本第一行的 #!/usr/bin/python 的解释,相信很多不熟悉 Linux 系统的同 ...

  4. jQuery---事件解绑与事件触发

    事件解绑与事件触发 $("p").off("click"); $("#btn").on("click", functio ...

  5. 数据库设计工具-powerdesigner

    一.PowerDesigner导出SQL,注释为空时以name代替 操作步骤 1. 2. 3.将Value中的内容全部替换为如下 %:COLUMN% [%National%?national ]%DA ...

  6. Educational Codeforces Round 80 (Rated for Div. 2)(A-E)

    C D E 这三道题感觉挺好       决定程序是否能通过优化在要求的时间内完成,程序运行时间为t,你可以选择花X天来优化,优化后程序的运行时间为t/(x+1)取上整,花费的时间为程序运行时间加上优 ...

  7. Leetcode 995. K 连续位的最小翻转次数

    题目: 在仅包含 0 和 1 的数组 A 中,一次 K 位翻转包括选择一个长度为 K 的(连续)子数组,同时将子数组中的每个 0 更改为 1,而每个 1 更改为 0. 返回所需的 K 位翻转的次数,以 ...

  8. EF Code-First(Oracle)通过Migration来更新数据库的表的字段

    关于Oracle中使用Entity Framework 6.x Code-First方式开发,请参考博客(菩提树下的杨过)https://www.cnblogs.com/yjmyzz/p/how-to ...

  9. RocketMQ的生产者和消费者

    生产者: /** * 生产者 */ public class Provider { public static void main(String[] args) throws MQClientExce ...

  10. BZOJ 3698: XWW的难题

    Description XWW是个影响力很大的人,他有很多的追随者.这些追随者都想要加入XWW教成为XWW的教徒.但是这并不容易,需要通过XWW的考核. XWW给你出了这么一个难题:XWW给你一个N ...