C++之数据对齐

news2024/9/26 3:22:20

数据对齐
• 总说:
○ 对齐方式:表示的是一个类型的对象存放的内存地址应满足的条件
○ 好处:对齐的数据在读写上有性能优势
○ 对于不对齐的结构体,编译器会自动补齐以提高CPU的寻址效率
• 数据对齐:(四个函数/描述符):结构体不对齐会有什么问题?
○ offsetof(X, x):X为结构体名(也可为对象名,实测对象名是不可以的),x为结构体成员,返回x在X中的偏移(以0开始),按字节的,按位是不行的——带虚函数的类计算不了偏移?
§ offsetof的实现:#define offset(s, m)((size_t)&reinterpret_cast<char const volatile&>(((s*)0)->m))
§ 这里有个问题:对m的访问为啥是偏移量,而不是真实值?
§ 你可能会迷惑,这样强制转换后的结构指针怎么可以用来访问结构体字段?呵呵,其实这个表达式根本没有也不打算访问m字段。ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((s*)0)的结果就是一个类型为s的NULL指针。如果利用这个NULL指针来访问s的成员当然是非法的,但&(((s)0)->m)的意图并非想存取s字段内容,而仅仅是计算当结构体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据s的内存布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过NULL指针访问内存的问题。又因为首址的值为0,所以这个地址的值就是字段相对于结构体基址的偏移。
○ alignof(X):查询X的对齐字节,由最大元素类型(,基础类型,如果有嵌套结构体,取该结构体中的最大基础类型比较)的字节数决定;数组的对齐值由其元素决定;如alignof(char)就是1
§ 参数X:自定义类型或内置类型或者变量,不完整类型编译不过
§ 返回std::size_t类型值
§ alignof是Microsoft的运算符
§ 引用与其引用的数据b对齐值相同
○ 对齐描述符alignas:重新设定结构体的对齐方式;既可以接收常量表达式,也可以接受类型作为参数;
§ 使用常量表达式作为alignas的操作符时结果必须是以2的幂次作为对齐值,设定的对齐值如果小于默认对齐则会忽视
§ C++11标准中规定了一个“基本对齐值”,一般情况下等于平台支持的最大标量数据类型的对齐值,可以通过alignof(std::max_align_t)来查询其值 (#include)
§ 在C++11之前,使用编译器的扩展来描述对齐方式,如gnu的__attribute
((aligned(8)))
使用示例:
alignas(x) char c //指定c的对齐是x
struct alignas(x) XX { A xx; } // 指定结构体对齐字节为x
○ C++11对于对齐的支持不限于alignof操作符和alignas描述符,STL中的内建函数std::align函数来动态指定的对齐方式调整数据块的位置(待验证)
• 通过属性(属性是对语言中的实体对象如函数变量等附加的一些额外注解信息,用来实现功能或代码优化):
○ linux:通过GNU的关键字__attribute__来声明:attribute((attribute-list)),详细参考gcc在线文档: https://gcc.gnu.org/onlinedocs/
○ windows:__declspec,如控制变量的对齐方式:__declspec(align(x))
○ C++11的通用属性:[[ attribute_list]]
§ 语法上,通用属性可以作用于类型,变量,名称,代码块等。对于作用于声明的通用属性,既可以写在声明的起始处,也可写在声明的标识符之后;对作用于整个语句的通用属性,应该写在语句起始处
§ 现有C++11标准中只预定义了两个通用属性:
□ [[noreturn]]:用于标识不会返回的函数(不会返回的函数和返回void不同,不会返回的函数在被调用完成后后续代码不会再被执行),主要用于标识那些不会将控制流返回给原调用函数的函数,如终止应用程序的函数,异常抛出函数等,好处有利于编译器进行优化,但要谨慎使用——待验证
□ [[carries_dependency]]:和并行情况下的编译期优化有关,主要是用来解决弱内存模型平台上使用memory_order_consume内存顺序枚举问题
• 结构体对齐原则:
○ 结构体成员变量是基本类型:结构体中的第一个成员位置在偏移量0,之后每个变量的偏移量必须是它本身字节数的整数倍;
○ 如果结构体中嵌套结构体,那么嵌套结构体成员的偏移量必须是它最大成员的字节数的整数倍。注意:1.不是按结构体整体的大小偏移;2.不是展开后偏移,整个结构体的偏移量是包括嵌套结构体在内的最大成员的字节数
○ 如果是继承关系,目测和嵌套结构体一样
○ 结构体的总大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的整数倍,这样在处理数组时可以保证每一项都边界对齐。,在最后还会根据需要自动填充空缺的字节
○ 强制对齐:编译器提供了#pragma pack(n)来设定变量以n字节对齐方式:
§ 如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式;
§ 如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
§ 结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
§ 注意:这个预编译命令会不论大小都会改变对齐值,alignas只会改大不会改小
#pragma pack(push) //保存对齐状态
#pragma pack(4) //设定为4字节对齐,n=1,2,4,8,16改变系统的对齐系数
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop) //恢复对齐状态
在linux下测试好像一直都有效;
单独使用#pragma pack(x)在vs下测试会使后面定义的结构体都按x对齐;

在C语言里有一个机制是内存对齐,当然不止C语言,包括其他的编程语言都会有内存对齐机制,否则编译出来的软件无法正常运行,至于为什么呢?众所周知,在内存中,所有的数据都是按字节为最小单位存储的,存储单位称为存储单元,所以也叫内存是由很多存储单元组成的,这些存储单元(字节)都有固定的地址标示着(这里说的非虚拟模式下),在我们程序员眼里内存就是一个一个字节组成的,这些字节对应的地址都是连续并排好的,但是在CPU中不是,在CPU看来内存是一段一段的,每段大小取决于CPU的寻址位宽!
比如在C语言里申请了short和两个char变量:

Shor a;
Char b;
Char c;

在内存中对应的起始地址为:0x01(a),0x03(b),0x04©
在我们程序员眼里在内存中的存储是这样的:
在这里插入图片描述

但是在一个寻址位宽为32位的CPU上是这样的:
在这里插入图片描述

Short占用16个bit位char占用8个bit位所以16+8+8=32 刚好组成一段!

但是如果在c语言中我们这样定义了三个变量:

Short a;(起始地址:0x01)
Char b;(起始地址:0x03)
Short c;(起始地址:0x04)

那么如果C语言编译器不为我们自动对齐在内存中就会这样:
在这里插入图片描述
可以看到最后一个short的地址被分两个段存储了,也就是说如果CPU要读取变量c那么必须先将第一个段里的数据全部读取出来送到寄存器里(这里假定通用寄存器为32位),然后将第一个寄存器里的0x01到0x03的数据剔除掉,将0x04上的bit位数据挪移到寄存器的低位上,然后在去读取第二个段上的地址数据也送到寄存器里,然后将0x06到0x08上的数据剔除掉,这样就将额外的数据剔除掉了,但是这样就会读取两次,浪费更多的时间1,为什么不直接从0x04开始读取?注意CPU不可以从某段的段偏移上读取只能从某段的起始地址开始读取!这是虚拟内存中的概念(详细参见虚拟内存的映射关系),不然的话就会造成读取时出现数据越界的情况!

假如0x05地址是栈的尾地址,如: 0x04 0x05 0x06 0x07
那么在一个32位的CPU下寻址时就会发生越界的情况,访问到其他进程下的内存则会被操作系统里内核中断代码捕捉会被视为恶意代码会立即被中断,当然部分CPU也并不会全部都给你按上面的方式来读取,假如遇到了上图那样的地址存储方式,CPU会直接报硬件中断,直接罢工不读取,然后由操作系统捕捉这个硬件中断直接咔擦掉你的程序!在调试情况下调试器捕捉这个消息时会中断!
所以为了解决这一问题,编译器使用了一种叫做空间交换法的方式来对齐内存,也就是说增加额外的内存,换取时间上的效率,同时也避免部分CPU的硬件中断!
下面是C语言内存对齐后的地址:
在这里插入图片描述

可以看到char为了对齐short增加了一个字节,而short为了对齐32位寻址增加了两个字节,这就是空间上的时间交换法,这样的话CPU读取时只需要一次就可以读取完!注意增加的字节只是为了对齐寻址,额外的字节是不允许被赋值的,虽然说可以赋值,但是当我们赋值是编译器不会允许我们向额外的地址传递数据!注意以上内存对齐是发生在结构体当中的,不光是结构体,在函数体里所有的变量包括代码段内存全部都会被内存对齐,函数体里的对齐方式不同于结构体,博主也没有去深刻的去查函数体里的对齐方式,这里也不必要做过多的了解因为编译器都会帮我们做好!只是在个别面试时会面试到关于内存对齐的基础题!

这种方法就是空间上的时间转换,牺牲额外的空间换取时间上的效率,毕竟在这个内存越来越大,价格越来越便宜的年代,牺牲额外的内存换取效率也是一件可行之事!

每个编译器里都有一个内存对齐的模数,这个摸数取决于你的CPU位宽!我们可以通过# #pragma pack(n)来强制改变它,n的可取值范围是:n=1,2,4,8,16(字节),当然这里也不是特别建议去改变它的默认寻址宽度,因为超出的话CPU没有办法一次性读取完还是会分段裁剪来读,过小的话CPU也是要去分两次或者更多的次数然后裁剪的读取,所以建议为默认值

参考博客:https://blog.csdn.net/bjbz_cxy/article/details/78418828

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

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

相关文章

【shell】shell结程规范与变量

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、shell脚本概述1.Shell脚本应用场景2.Shell的作用3.用户的登录Shell 二、Shell编程规范1.she11脚本的构成2.运行shel1脚本3.重定向与管道操作1.交互式硬件设备2.…

学习CTF一定要知道的网站!快快收藏!

今天在这里给大家介绍一些好用的学习网站&#xff1a; 首先是CTF做题平台&#xff1a; •BUUCTF •攻防世界 •ctf.show •catf1ag.cn •ctfhub.com •ctf.wgpsec.org •ctf-wiki.org •ctftime.org 学习论坛&#xff1a; •https://www.52pojie.cn/ •https://www.freebuf.…

【nvm 安装】使用nvm管理node版本,安装步骤

一、下载nvm 下载链接&#xff1a;Releases coreybutler/nvm-windows GitHub 建议下载安装版的&#xff0c;非安装版还需要额外配置环境变量。 二、安装 注意 &#xff1a; 都在说安装之前需要删除node版本&#xff0c;避免冲突&#xff0c;很听话的我删除了&#xff0c;…

每日学术速递5.4

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.IndoorSim-to-OutdoorReal: Learning to Navigate Outdoors without any Outdoor Experience 标题&#xff1a;IndoorSim-to-OutdoorReal&#xff1a;在没有任何户外经验的情况下学…

Linux系统中ARMv8架构u-boot启动流程分析

目录 ​ 本文基于 armv8 架构来对 u-boot 进行启动流程分析。 1 概述 2 armv8 u-boot的启动 3 u-boot源码整体结构和一些编译配置方式 3.1 编译配置方式 3.2 u-boot源码结构 4 u-boot armv8链接脚本 4.1 u-boot.lds 4.2 u-boot-spl.lds 本文基于 armv8 架构来对 u-bo…

Android LifecycleService

监听Service的生命周期-LifecycleService 为了方便我们对Service生命周期的监听&#xff0c;Android提供了一个名为LifecycleService的类&#xff0c;让该类继承自Service&#xff0c;并实现LifecycleOwner接口。 /*** A Service that is also a {link LifecycleOwner}.*/ pu…

项目实战——获取树形结构

获取树形结构 一、背景介绍二、 思路和方案方案一&#xff1a;使用递归查询的方式并构建树形结构方案二&#xff1a;使用临时表的方式构建树形结构使用临时表的优缺点 三、过程项目案例核心代码 四、总结五、升华 一、背景介绍 我们在开发中时常会遇到需要用到树形结构这种表示…

1分钟学会、3分钟上手、5分钟应用,快速上手责任链框架详解 | 京东云技术团队

作者&#xff1a;京东物流 覃玉杰 1. pie 简介 责任链模式是开发过程中常用的一种设计模式&#xff0c;在SpringMVC、Netty等许多框架中均有实现。我们日常的开发中如果要使用责任链模式&#xff0c;通常需要自己来实现&#xff0c;但自己临时实现的责任链既不通用&#xff0…

解决安装nrm,执行nrm ls时出现的const open=require(‘open’)问题

最开始安装的淘宝镜像源为npm config set registryhttps ://registry.npm.taobao.org/&#xff0c;后来看到镜像源变了&#xff0c;就换了下面的&#xff0c; 下载新的npm淘宝镜像资源包npm config set registry http://registry.npmmirror.com 查看&#xff0c;安装成功&…

【c语言】字符串的基本概念 | 字符串存储原理

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ …

Spring常用注解总结

目录 一、前言1、xml和注解的最佳实践&#xff1a;2、使用注解唯一需要注意的就是&#xff0c;必须开启注解的支持&#xff1a; 二、Spring的常用注解1、给容器中注入组件2、注入bean的注解3、JsonIgnore4、初始化和销毁方法5、Java配置类相关注解6、切面&#xff08;AOP&#…

DNS资源记录详解

资源记录&#xff08;resourse record&#xff09;就是域名服务器保存的记录&#xff0c;也是解析器请求的内容&#xff0c;资源记录保存在zone文件中。域&#xff08;domain&#xff09;&#xff1a;以 www.baidu.com 为例&#xff0c;com是一个域。baidu.com是一个域&#xf…

Selenium:三种等待方式

目录 一、显示等待 二、隐式等待 三、强制等待 UI自动化测试&#xff0c;大多都是通过定位页面元素来模拟实际的生产场景操作。但在编写自动化测试脚本中&#xff0c;经常出现元素定位不到的情况&#xff0c;究其原因&#xff0c;无非两种情况&#xff1a;1、有frame&#x…

Unity 光照

\\\\\\\ Unity烘焙&#xff08;Baking&#xff09;是指将场景中的动态光照转换为静态贴图。在烘焙过程中&#xff0c;Unity会将场景中的光源、材质和对象等信息计算出来&#xff0c;并存储为贴图。当玩家进入场景时&#xff0c;Unity只需要读取这些预计算好的贴图或者数据文件&…

2023年计算机视觉与模式识别国际会议(CCVPR 2023)

会议简介 Brief Introduction 2023年计算机视觉与模式识别国际会议(CCVPR 2023) 会议时间&#xff1a;2023年9月15日-17日 召开地点&#xff1a;英国牛津 大会官网&#xff1a;www.ccvpr.org 计算机视觉技术与模式识别是现代科学中备受关注的热点技术&#xff0c;它的革新对各行…

Monorepo开发策略详解

目录 一&#xff1a;什么是 Monorepo&#xff1f; 二&#xff1a;Monorepo 和其他结构的区别&#xff1a; 三&#xff1a;Monorepo的优缺点 3.1.优点 3.2.缺点 四&#xff1a;如何使用Monorepo 一&#xff1a;什么是 Monorepo&#xff1f; Monorepo 是一种将多个项目存放…

【iOS】—— RunLoop初学

RunLoop 文章目录 RunLoopRunLoop简介RunLoop基本使用Runloop伪代码Runloop模型图 Runloop对象Runloop对象的获取_CFRunLoopGet0方法 RunLoop的相关类RunLoop相关类的实现CFRunLoopRefCFRunLoopModeRef五种运行模式CommonModes什么是Mode Item&#xff1f;Mode到底包含哪些类型…

【SWAT水文模型】SWAT水文模型建立及应用第四期: 气象数据的准备(中国区域高精度同化气象站CMADS)

SWAT水文模型建立及应用&#xff1a; 气象数据的准备 1 简介2 气象数据的准备&#xff08;中国区域高精度同化气象站CMADS&#xff09;2.1 数据说明2.2 数据下载 3 CMADS 数据集SWAT子集使用说明3.1 SWAT2009版本3.2 SWAT2012版本 参考 本博客主要介绍气象数据的准备&#xff0…

HulaCWMS呼啦企业网站管理系统 v3.0.4

源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/87630654 HulaCWMS(呼啦企业网站管理系统)是基于ThinkPHP5框架开发&#xff0c;安全高效&#xff0c;包括ThinkPHP5的所有特性。专注于企业、政府单位网站建设&#xff0c;以免费开源的方式&#xff0c;帮…

python - 模块使用详解

前言 Python有非常强大的第三方库&#xff0c;也有非常多的内置模块帮助开发人员实现某些功能&#xff0c;无需开发人员自己造轮子。本文介绍Python的模块。 什么是模块 模块简单来说就是一系列功能的集合体&#xff0c;如果将程序的开发比喻成拼图&#xff0c;模块就是各种…