内存对齐的原理和使用
1. 什么是内存对齐?
内存对齐是指将数据存储在内存中时,按照数据类型的大小,将数据放在特定的内存边界上。例如,4 字节的 int 通常放在能够被 4 整除的地址上,8 字节的 double 则放在能被 8 整除的地址上。
2. 为什么需要内存对齐?
内存对齐有几个关键的原因:
2.1 提高 CPU 访问速度
- CPU 访问内存时,会一次性读取一定数量的字节(通常是 4、8 或 16 字节)。当数据地址是对齐的,CPU 可以在一次内存读取操作中完整读取数据,这显著提升了效率。
- 如果数据没有对齐,CPU 可能需要执行多次内存读取操作来获取完整的数据。例如,如果一个 4 字节的
int存储在一个未对齐的位置(比如 3 字节边界上),那么 CPU 需要读取两次内存块,将其拼接成一个完整的数据。这会降低系统性能。
2.2 硬件要求
- 一些处理器架构(如早期的 RISC 处理器或某些嵌入式系统)不允许从非对齐的内存地址读取数据。如果数据没有对齐,硬件可能会抛出错误,导致程序崩溃。
- 对于允许非对齐访问的架构(如大多数 x86 处理器),虽然可以处理非对齐数据,但这往往伴随着性能上的显著开销。
2.3 避免额外的计算复杂性
- 如果数据不对齐,编译器和硬件需要额外计算如何正确读取和写入数据。这增加了编译器和 CPU 的复杂性,也可能会导致更高的功耗。
3. 内存对齐的原理
内存对齐遵循以下基本规则:
- 数据的起始地址必须是它大小的整数倍。例如,4 字节的
int变量的地址通常是 4 的倍数,8 字节的double变量的地址是 8 的倍数。 - 现代编译器通常会为不同类型的数据设置默认的对齐规则,确保数据存储在合适的内存地址上。
4. 内存对齐的代价:填充字节
为了满足对齐要求,编译器可能会在内存中引入一些填充字节(padding bytes),这些字节不会存储有效数据,但它们确保后续的数据地址是对齐的。
示例:
假设有一个包含不同类型成员的结构体:
struct Example {char a; // 1字节int b; // 4字节short c; // 2字节
};
在 32 位系统上,内存对齐可能是这样的:
| a (1 byte) | padding (3 bytes) | b (4 bytes) | c (2 bytes) | padding (2 bytes) |
a占 1 字节,但b是 4 字节的int,需要对齐到 4 字节边界,因此a后面有 3 个填充字节。c是 2 字节的short,为了对齐结构体的总大小,也可能引入额外的 2 字节填充。
总大小为 12 字节,而不是简单的 7 字节。
5. 内存对齐的影响
- 性能提升:对齐使得 CPU 能够更高效地访问内存,尤其是当大量数据处理时,这一点显得尤为重要。
- 空间开销:虽然内存对齐可能引入填充字节,增加内存使用,但这通常是必要的权衡,因为提升了数据访问效率。
6. 手动控制对齐
在 C++ 中,可以使用 #pragma pack 或 alignas 关键字来手动调整结构体的对齐方式。例如,#pragma pack(1) 可以让编译器不插入填充字节,减少内存占用。但这可能会牺牲性能,甚至导致某些平台上的错误。
7.使用#pragma pack 来关闭内存对齐
手动控制内存对齐手动控制内存对齐是一种手段,可以通过编译器指令调整数据结构的内存对齐方式,以减少内存浪费或满足特殊的内存布局需求。通常情况下,编译器会自动为数据类型安排合理的对齐,但在某些场景下,你可能需要手动修改对齐策略。常用的手段包括 #pragma pack 和 alignas 关键字。下面详细解释这两种方法:
1. #pragma pack
#pragma pack 是一种预处理指令,用来控制结构体或类的对齐方式。它可以通过设定字节边界来调整结构体中的填充字节。
语法:
#pragma pack(n)
- 其中
n表示对齐字节边界,例如1、2、4、8等。编译器会强制将结构体成员对齐到n字节的边界上。
示例:
#pragma pack(1)
struct Example {char a; // 1字节int b; // 4字节short c; // 2字节
};
#pragma pack() // 恢复默认对齐
在 #pragma pack(1) 下,编译器将强制所有成员不插入任何填充字节,对齐到 1 字节边界,结构体内存布局如下:
| a (1 byte) | b (4 bytes) | c (2 bytes) |
此时,整个结构体大小为 7 字节,没有任何填充字节。
不使用 #pragma pack 的默认布局:
如果不使用 #pragma pack(1),编译器会按照默认对齐方式处理。以 4 字节对齐为例,Example 结构体的默认内存布局为:
| a (1 byte) | padding (3 bytes) | b (4 bytes) | c (2 bytes) | padding (2 bytes) |
默认情况下,int b 需要对齐到 4 字节边界,因此在 a 后面插入了 3 个字节的填充,使得 b 正好对齐到 4 字节边界,最后结构体大小为 12 字节。
优缺点:
- 优点:可以减少不必要的内存填充,尤其在数据存储、网络传输等场景下,可以节省内存。
- 缺点:强制对齐到小字节边界可能导致性能下降,尤其是在现代 CPU 上,非对齐的内存访问可能会引起多次内存访问,导致系统性能变差。
8 使用alignas 关键字
alignas 是 C++11 引入的关键字,它可以在声明变量或结构体成员时,指定某个成员的对齐方式。
语法:
alignas(n) type var;
- 其中
n是对齐要求的字节数,type是变量类型,var是变量名称。alignas可以为数据结构的某个特定成员或整个结构体设置对齐。
示例:
struct Example {char a; // 1字节alignas(8) int b; // 强制 b 对齐到 8 字节short c; // 2字节
};
在这个例子中,int b 强制对齐到 8 字节边界。内存布局将变为:
| a (1 byte) | padding (7 bytes) | b (4 bytes) | c (2 bytes) | padding (2 bytes) |
整个结构体大小为 16 字节。相比默认情况下的对齐方式(4 字节对齐),b 现在被强制对齐到 8 字节,因此插入了更多的填充字节以确保对齐。
对齐整个结构体:
alignas 还可以用于整个结构体的对齐,例如:
struct alignas(16) Example {char a;int b;
};
这会确保 Example 结构体的起始地址是 16 字节对齐的,通常在需要满足特定硬件需求或 SIMD(单指令多数据)操作时会使用。
优缺点:
- 优点:
alignas提供了更细粒度的控制,允许为特定变量或结构体自定义对齐方式。 - 缺点:过度使用可能增加填充字节,从而浪费内存;同时,也会影响性能,尤其是在对齐过度的情况下。
3. #pragma pack 和 alignas 的比较
- 适用范围:
#pragma pack主要用于控制整个结构体的对齐方式,而alignas可以细粒度控制特定成员或整个结构体的对齐。 - 灵活性:
alignas更灵活,因为它可以精确控制特定成员的对齐,而#pragma pack是全局性的。 - 兼容性:
#pragma pack是一种编译器指令,不同编译器可能有不同的实现和支持;alignas是标准的 C++ 关键字,跨平台兼容性更好。
相关文章:
内存对齐的原理和使用
1. 什么是内存对齐? 内存对齐是指将数据存储在内存中时,按照数据类型的大小,将数据放在特定的内存边界上。例如,4 字节的 int 通常放在能够被 4 整除的地址上,8 字节的 double 则放在能被 8 整除的地址上。 2. 为什么…...
搭建企业级私有仓库harbor
华子目录 harbor简介实验环境准备下载软件包安装docker-cehosts解析 实验步骤配置https加密传输解压进入解压目录,修改文件配置启动harbor 测试客户端配置harbor本地加速器注意 通过docker compose管理harbor harbor简介 harbor是由wmware公司开源的企业级docker r…...
互联网前后端分离的开发场景,一般会员和数据权限的判断是放在前端还是后端?
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...
李宏毅机器学习2022-HW8-Anomaly Detection
文章目录 TaskBaselineReportQuestion2 Code Link Task 异常检测Anomaly Detection 将data经过Encoder,在经过Decoder,根据输入和输出的差距来判断异常图像。training data是100000张人脸照片,testing data有大约10000张跟training data相同…...
用户体验分享 | YashanDB V23.2.3安装部署
近期崖山新版体验过程中,总能看到用户提问:openssl版本问题、monit命令找不到问题、yashan用户权限问题、数据库重装问题 今日整理了多位用户的安装经验,希望能够帮助到大家~ 1.Lucifer三思而后行 :YashanDB 个人版数据库安装部…...
【漏洞复现】泛微OA E-Office /E-mobile/App/init.php 任意文件上传漏洞
免责声明: 本文旨在提供有关特定漏洞的信息,以帮助用户了解潜在风险。发布此信息旨在促进网络安全意识和技术进步,并非出于恶意。读者应理解,利用本文提到的漏洞或进行相关测试可能违反法律或服务协议。未经授权访问系统、网络或应用程序可能导致法律责任或严重后果…...
SpringCloudEureka实战:搭建EurekaServer
1、依赖引入 <dependencies><!-- 注册中心 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency> </dependencies> <de…...
DataLight(V1.4.5) 版本更新,新增 Ranger、Solr
DataLight(V1.4.5) 版本更新,新增 Ranger、Solr DataLight 迎来了重大的版本更新,现已发布 V1.4.5 版本。本次更新对平台进行了较多的功能拓展和优化,新增了对 Ranger 和 Solr 服务组件的支持,同时对多项已…...
深度解析:Python蓝桥杯青少组精英赛道与高端题型概览
目录 一、蓝桥杯青少组简介二、赛项组别与年龄范围三、比赛内容与题型1. 基础知识范围2. 题型设置2.1 选择题2.2 编程题 3. 考试时长 四、奖项设置与激励措施五、总结 一、蓝桥杯青少组简介 蓝桥杯全国软件和信息技术专业人才大赛(简称“蓝桥杯”)是由工…...
如何使用SCCMSecrets识别SCCM策略中潜在的安全问题
关于SCCMSecrets SCCMSecrets是一款针对SCCM策略的安全扫描与检测工具,该工具旨在提供一种有关 SCCM 策略的全面安全检测方法。 该工具可以从各种权限级别执行,并将尝试发现与策略分发相关的潜在错误配置。除了分发点上托管的包脚本外,它还将…...
Qt 信号重载问题--使用lambda表达式--解决方法
在connect()中,使用lambda表达式时遇到信号重载,无法识别使用哪个参数时,可通过以下方法处理: 1. 使用QOverload: Qt5.7才有 connect(comboBox,QOverload<int>::of(&QComboBox::currentIndexChanged), [](int index)…...
并行编程实战——TBB框架的应用之一Supra的基础
一、TBB的应用 在前面分析了TBB框架的各种基本知识和相关的基础应用。这些基础的应用很容易通过学习文档或相关的代码来较为轻松的掌握。为了能够更好的理解TBB框架的优势,这里从一个开源的应用程序来分析一下TBB在其中的更高一层的抽象应用,以方便开发…...
std::vector
std::vector是C标准库中一个非常强大的容器类,它提供了动态数组的功能。std::vector可以自动调整大小,提供了随机访问的能力,同时还支持在序列的尾部高效地添加和删除元素。 当创建一个空的std::vector对象时,它不分配任何内存&a…...
Java Web 之 Cookie 详解
在 JavaWeb 开发中,Cookie 就像网站给浏览器贴的小纸条,用于记录一些用户信息或状态,方便下次访问时识别用户身份或进行个性化服务。 也可以这么理解: 场景一:想象一下,你去一家咖啡店,店员认…...
linux系统下让.py文件开机自启动
一 创建服务文件 1、打开终端 2、切换到root用户 sudo su3、创建一个新的systemd服务文件 nano /etc/systemd/system/total_test0619.service 4、在服务文件中添加以下内容 [Unit] DescriptionRun total_test0619.py at startup[Service] Typesimple ExecStart/usr/bin/n…...
linux远程桌面:xrdp 安装失败
window 如何远程 Linux 桌面 安装xrdp yum install xrdpsystemctl start xrdp 如果找不到软件包,就安装epel源,最好改成国内镜像的 在 /etc/yum.repos.d/ 下创建epel.repo,内容如下 [epel] nameExtra Packages for Enterprise Linux 7 - $basearch …...
9.30Python基础-元组(补充)、字典、集合
Python元组(tuple)补充 1、元组的不可变性 元组(tuple)是Python中的一种内置数据类型,用于存储不可变的序列。虽然元组本身不可变,但元组内的元素如果是可变对象(如列表)ÿ…...
桥接模式和NET模式的区别
桥接模式和NET模式的区别 NAT模式: NAT:网络地址转换(模式):借助宿主机来上网,没桥接那么麻烦,只用配置DNS即可。 缺点:扎根于宿主机,不能和局域网内其它真实的主机进行…...
Pigar:Python 项目的依赖管理利器
🌟 引言 在Python项目开发过程中,依赖管理是一个不可忽视的环节。一个精确且易于维护的requirements.txt文件对于项目的部署和协作至关重要。今天,我们将介绍一款名为Pigar的自动生成requirements.txt文件的依赖管理工具,它通过一…...
泰勒图 ——基于相关性与标准差的多模型评价指标可视化比较-XGBoost、sklearn
1、基于相关性与标准差的多模型评价指标可视化比较 # 数据读取并分割 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split plt.rcParams[font.family] = Times New Roman plt.rcParams[axes.unic…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
