数据对齐

程序中变量的存储可以直接影响到程序的运行速度。计算机中都是以字节划分内存空间,通常编译器会为我们选择适合目标平台的对齐策略,但是有时候也带了一些麻烦,要自定义变量的对齐策略我们就需要用到#pragma pack

应用场景

百度百科里提到一个典型的应用场景:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。

说白了,一个正确的字节对齐方式,就是为了让CPU在最短的时间内读取完变量,同时还让整体的结构存储空间最小。

字节对齐方式的使用

四个概念

  • 数据类型自身的对齐值:基本数据类型的自身对齐值。
  • 结构体或者类的自身对齐值:其数据成员中自身对齐值最大的那个值。
  • 指定对齐值:系统默认的或者我们通过#pragma pack(value)指定对齐值value。
  • 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

想要巧妙设定字节对齐的字节数,必须要知道上面四个数值之间的关系:数据成员、结构体和类的有效对齐值,是最终存储时的数据、结构体和类决定数据存放地址方式的值。分为数据有效对齐值,结构体和类有效对齐值两个。同时整体的要满足这四条规则。

  1. 数据有效对齐值 = min(数据类型的自身对齐值,指定对齐值value)。
  2. 结构体,联合体和类有效对齐值 = min(结构体、联合体和类自身对齐值,指定对齐值value)
  3. 保证每一个数据满足:不同类型数据都满足:当前数据存储的首地址 % 数据有效对齐值 = 0;
  4. 补齐末尾的空间,保证长度对齐,即总长度 % 实际对齐长度 = 0;

选择最好的对齐字节数值

最好的value的值,就是和变量自身对齐值为整数数关系下,使得总体数据结构占用空间最小的值。

//默认字节对齐方式为4字节对齐
struct C {char b;  //自身对齐值为1,有效对齐值为min(1,4)int a;   //自身对齐值为4,有效对齐值为min(4,4)short c;  //自身对齐值为2,有效对齐值为min(2,4)
};
//由上述第2条:C的有效对齐值为4。

数据结构大小计算:

  • 我们认为第一个元素的起始地址为0x0000,所以,第一个char b的有效对齐值为1,0x0000%1=0,符合。
  • 第二个元素int a,有效对齐值为4,初始位置为0x0001,0x0001%4=1,不符合,往后面移动一位,0x0002%4=2,不符合,到0x0004,0x0004%4=0,符合,所以int a的起始地址为0x0004。
  • 第三个元素short c,有效对齐值为2,起始地址为0x0008,0x0008%2=0,符合,所以short c起始地址为0x0008
  • 所以结构体C的整体存储空间大小为:4+4+2=10,10%4=2,所以要满足上面的第四条,则0x0010和0x0011也是C的空间,这样C一共就占了12个字节。

但是明显这个不是最优的字节对齐方式,看看下面这个:

//改变字节对齐方式为2字节对齐
struct C {char b;  //自身对齐值为1,有效对齐值为min(1,2)int a;   //自身对齐值为4,有效对齐值为min(4,2)short c;  //自身对齐值为2,有效对齐值为min(2,2)
};
//由上述第2条:C的有效对齐值为2。

数据结构大小计算:

  • 第一个元素的起始地址为0x0000,char b的有效对齐值为1,0x0000%1=0,符合。
  • 第二个元素int a,有效对齐值为2,初始位置为0x0001,0x0001%2=1,不符合,往后面移动一位,0x0002%2=0,符合,所以int a的起始地址为0x0002。
  • 第三个元素short c,有效对齐值为2,起始地址为0x0006,0x0006%2=0,符合,所以short c起始地址为0x0006。
  • 所以结构体C的整体存储空间大小为:2+4+2=8,8%2=0,所以满足上面的第四条,这样C一共就占了8个字节。

所以,设置不同的字节对齐方式对于数据的存储空间来说有不同的影响,在和变量自身对齐值为整数数关系下,选择变量有效对齐值中位数附近的数值效果最好。

#pragma pack

作用:

指定结构体,联合,类成员的字节对齐方式

语法:

#pragma pack( show | push | pop ,identifier, n )
  • show:

    • #pragma pack(show):显示当前字节对齐方式的字节数,以warning显示。
  • push:
    • #pragma pack(push):将当前的字节对齐方式入栈(internal compiler stack),也就是保存当前的字节对齐方式。
  • pop:
    • #pragma pack(pop):将(internal compiler stack)栈顶的记录出栈,也就是恢复最新的一次字节对齐方式。
  • identifier:
    • #pragma pack(push,identfier):当同push一起使用时,赋予当前被压入栈中的record一个名称;
    • #pragma pack(pop,identfier):当同pop一起使用时,从internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作。
  • n:
    • #pragma pack(n):指定n个字节对齐,合法数值为2的非负整数次幂: 1、2、4、8、16,缺省时为8。
    • #pragma pack(push,n):当同push一起使用时,将当前的字节对齐方式入栈,同时设置当前的字节对齐数值为n
    • #pragma pack(pop,n):当同pop一起使用时,将栈顶的字节对齐方式出栈,同时设置当前的字节对齐数值为n

使用用例

设置字节对齐方式为2字节对齐:

#pragma pack(2)
struct C {char b;  //自身对齐值为1,有效对齐值为min(1,2)int a;   //自身对齐值为4,有效对齐值为min(4,2)short c;  //自身对齐值为2,有效对齐值为min(2,2)
};
#pragma pack()//恢复默认字节对齐方式

#pragma pack详解相关推荐

  1. python place布局_Python TKinter布局管理Place()Grid Pack详解

    Tkinter是Python标准GUI工具包,有三种布局管理方式: pack grid place 这三种布局管理在同一个 master window 里是不可以混用的. 下面通过三示例,详解使用参数 ...

  2. #pragma用法详解

    #pragma预处理命令 #pragma可以说是C++中最复杂的预处理指令了,下面是最常用的几个#pragma指令: #pragma comment(lib,"XXX.lib") ...

  3. #pragma命令详解

    每种C和C++的实现支持对其宿主机或操作系统唯一的功能.例如,一些程序需要精确控制超出数据所在的储存空间,或着控制特定函数接受参数的方式.#pragma指示使每个编译程序在保留C和C++语言的整体兼容 ...

  4. 预处理 #pragma 命令详解

    关注.星标公众号,不错过精彩内容 素材来源:网络 编辑整理:strongerHuang 预处理指令 #pragma 相信程序员都知道,但在所有的预处理指令中,#pragma 指令可能是最复杂的了,它的 ...

  5. python struct.unpack和struct.pack详解

    Python相关文档链接: https://docs.python.org/3/library/struct.html#format-characters 简介 struct模块用于将外部压缩的格式字 ...

  6. #pragma pack 用法详解

    #pragma pack 用法详解 pack为struct.union和class等的成员对齐指定字节边界,与编译选项(属性 -> 配置属性 -> C/C++ ->代码生成 -> ...

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

    前言 我们知道结构体内存对齐字节可以通过#pragma pack(n) 的方式来指定. 但是,有没有想过一个问题,某些时候我想4字节对齐,有些时候我又想1字节或者8字节对齐,那么怎么解决这个问题呢? ...

  8. pragma comment的使用 pragma预处理指令详解

    pragma comment的使用 pragma预处理指令详解 #pragma comment( comment-type [,"commentstring"] ) 该宏放置一个注 ...

  9. #pragma comment和#pragma 预处理指令详解

    该宏放置一个注释到对象文件或者可执行文件. 例如,#pragma   comment(lib,"Ws2_32.lib")表示链接Ws2_32.lib这个库. 和在工程设置里写上链入 ...

最新文章

  1. 在eclipse中将项目发布到tomcat的root目录
  2. nvcc gcc g++混合编译器编程
  3. C语言从0到1·数据类型及其存储
  4. wireshark捕获不到东西_好书分享——Wireshark从入门到精通
  5. gcc/g++编译器的优化
  6. 图机器学习 | 图信号处理、矩阵分解、随机游走和深度学习算法
  7. configure 包,出现error: no acceptable C compiler found in $PATH 问题
  8. 浅析GDAL库C#版本支持中文路径问题
  9. python怎么看待_如何看待将Python作为少儿编程的基础语言?
  10. python、MongoDB安装
  11. [Vue CLI 3] 环境变量和模式配置实践与源码分析
  12. C语言学习-翁凯(第八章笔记)
  13. 用HOOK机制让自绘菜单栏控件模拟系统菜单栏行为
  14. 拷贝相关器 matlab,基于MELP的水下实时语音通信机的研究与实现
  15. 这是?国内跨境电商降本增收营销神器
  16. 关于nodejs gm的各种各样的问题解决方法集合(中文乱码,non-conforming drawing,��Ч���� )
  17. Android地址选择器的实现
  18. win7连不上手机热点,怎么处理?
  19. 怎么将ppt文件压缩变小一点?
  20. 数论-模运算与同余的性质

热门文章

  1. VScode latex 写作小结
  2. 道闸系统需不需要服务器,道闸车牌识别系统车辆进出场管理方案
  3. 新概念2 课文和单词(7)
  4. 蓝湖切图后如何做成html,如何使用蓝湖 sketch for Mac设置切图?
  5. AttributeError: type object ‘h5py.h5.H5PYConfig‘ has no attribute ‘__reduce_cython__‘的解决方案
  6. 插优盘电脑计算机窗口闪,U盘插到电脑上,电脑怎么一闪一闪的?
  7. 永久实现jupyter notebook多行输出
  8. CheckBox复选框
  9. 杨校老师课堂之Maven下载与配置阿里云镜像
  10. dnf剑魂buff等级上限_DNF5.8新版buff换装提升整理以及装备选择,防止弯路