C++中低级内存操作
C++中低级内存操作
C++相较于C有一个巨大的优势,那就是你不需要过多地担心内存管理。如果你使用面向对象的编程方式,你只需要确保每个独立的类都能妥善地管理自己的内存。通过构造和析构,编译器会帮助你管理内存,告诉你什么时候需要进行内存操作。将内存管理隐藏在类中显著提高了可用性,这一点在标准库类中得到了很好的体现。
然而,在某些应用程序或遗留代码中,你可能会遇到需要更低级别地操作内存的情况。无论是出于遗留代码、效率、调试还是好奇心,了解如何操作原始字节总是有帮助的。
指针
C++编译器会使用指针的声明类型来允许你进行指针算术。如果你声明了一个指向整数的指针并将其增加1,那么这个指针在内存中前进的距离是整数的大小,而不是一个单一的字节。这种操作对数组最有用,因为数组包含的数据是类型一致且在内存中连续的。
例如,假设你在自由存储区声明了一个整数数组:
int* myArray { new int[8] };
你可能已经熟悉了以下用于设置索引为2的值的语法:
myArray[2] = 33;
通过使用指针算术,你也可以使用以下等效的语法,该语法获取到myArray
“向前两个整数”处的内存的指针,然后解引用它以设置该值:
*(myArray + 2) = 33;
作为访问单个元素的另一种语法,指针算术看起来可能不太吸引人。但其真正的力量在于,像myArray+2
这样的表达式仍然是一个指向整数的指针,因此可以表示一个更小的整数数组。
让我们通过一个使用宽字符串的例子来看看。宽字符串支持所谓的Unicode字符,以表示例如日语字符串。wchar_t
类型是一个可以容纳这种Unicode字符的字符类型,并且通常比char
要大;也就是说,它不仅仅是一个字节。要告诉编译器一个字符串字面量是一个宽字符串字面量,可以在其前面加上一个L
。
例如,假设你有以下宽字符串:
const wchar_t* myString { L"Hello, World" };
进一步假设你有一个函数,该函数接受一个宽字符串并返回一个包含输入字符串大写版本的新字符串:
wchar_t* toCaps(const wchar_t* text);
请记住,C风格的字符串是以零结尾的,即它们的最后一个元素包含\0
。因此,没有必要在函数中添加一个大小参数来指定输入字符串的长度。该函数只需不断地迭代字符串的各个字符,直到遇到\0
字符为止。
通过将myString
传递给toCaps()
函数,你可以将其全部转换为大写。然而,如果你只想部分地大写化myString
,你可以使用指针算术来仅引用字符串的后一部分。下面的代码通过仅向指针加7来调用toCaps()
函数,以处理宽字符串中的“World”部分,尽管wchar_t
通常超过1个字节:
toCaps(myString + 7);
另一个有用的指针算术应用涉及到减法。从同一类型的另一个指针中减去一个指针会给你两个指针之间指向类型的元素数量,而不是它们之间的绝对字节数。
自定义内存管理
在你将遇到的99%(有人可能会说100%)的情况中,C++中的内置内存分配功能是足够的。在幕后,new
和delete
完成了以适当大小的块分配内存、维护可用内存区域列表以及在删除时将内存块释放回该列表的所有工作。但是,当资源约束非常紧张,或者在非常特殊的条件下,例如管理共享内存,实施自定义内存管理可能是一个可行的选项。不用担心,这并不像听起来那么可怕。
基本上,自己管理内存意味着类会分配一大块内存,并根据需要将该内存分配出去。这种方法有什么好处呢?管理自己的内存可能会减少开销。当你使用new
来分配内存时,程序还需要预留一小部分空间以记录分配了多少内存。这样,当你调用delete
时,可以释放适当数量的内存。对于大多数对象,开销比分配的内存小得多,因此影响甚微。然而,对于小对象或有大量对象的程序,开销可能会产生影响。当你自己管理内存时,你可能会提前知道每个对象的大小,因此你可能能够避免每个对象的开销。对于大量的小对象来说,差异可能是巨大的。
执行自定义内存管理需要重载new
和delete
操作符。
垃圾收集
在支持垃圾收集的环境中,程序员很少(如果有的话)明确释放与对象关联的内存。相反,不再有任何引用的对象将在某个时候被运行时库自动清理。垃圾收集没有像在C#和Java中那样内置到C++语言中。在现代C++中,你使用智能指针来管理内存,而在遗留代码中,你将看到通过new
和delete
在对象级别进行内存管理。
诸如shared_ptr
这样的智能指针提供了与垃圾收集内存非常相似的东西;也就是说,当某一资源的最后一个shared_ptr
实例被销毁时,该资源也会在那个时刻被销毁。在C++中实现真正的垃圾收集是可能但不容易的,但释放自己从释放内存的任务中可能会引入新的问题。
标记-清除垃圾收集
一种垃圾收集的方法被称为“标记-清除”(Mark and Sweep)。在这种方法下,垃圾收集器会周期性地检查程序中的每一个指针,并标注哪些内存仍然在使用中。在周期结束时,任何没有被标记的内存被认为是不再使用的,并会被释放。在C++中实现这样的算法并不简单,如果做错了,可能比使用delete
更容易出错!
尽管在C++中已经有了安全且简单的垃圾收集机制的尝试,但即使有了完美的C++垃圾收集实现,也不一定适用于所有应用程序。垃圾收集的缺点包括:
- 当垃圾收集器正在运行时,程序可能变得无响应。
- 在使用垃圾收集器的情况下,你会遇到所谓的“非确定性析构函数”。因为一个对象在被垃圾收集之前不会被销毁,所以析构函数在对象离开其作用域时不会立即执行。这意味着,由析构函数完成的资源清理(例如关闭文件、释放锁等)直到未来某个不确定的时间才会被执行。
编写垃圾收集机制是非常困难的。你很可能会做错,而且很可能会很慢。因此,如果你确实想在你的应用程序中使用垃圾收集内存,我强烈建议你研究现有的专门的垃圾收集库,并重用它们。
对象池
垃圾收集就像是为野餐购买盘子,并将用过的盘子留在院子里,以便某人在某个时候将它们捡起来并扔掉。肯定有更环保的内存管理方法。对象池就是回收的等价物。你购买了一定数量的盘子,使用过一个盘子后,你将其清洗,以便以后可以再次使用。
对象池是理想的解决方案,适用于你需要在一段时间内多次使用相同类型的多个对象,并且每次创建都会产生开销的情况。
相关文章:
C++中低级内存操作
C中低级内存操作 C相较于C有一个巨大的优势,那就是你不需要过多地担心内存管理。如果你使用面向对象的编程方式,你只需要确保每个独立的类都能妥善地管理自己的内存。通过构造和析构,编译器会帮助你管理内存,告诉你什么时候需要进…...
Linux硬盘大小查看命令全解析 (linux查看硬盘大小命令)
Linux操作系统是一款广泛应用于服务器和嵌入式设备的操作系统,相比于Windows等其他操作系统,Linux的优点之一就是支持强大的命令行操作。在日常操作中,了解和掌握一些简单但实用的命令可以提高工作效率。比如硬盘大小查看命令,在L…...
什么是供应链金融?
一、供应链金融产生背景 供应链金融兴起的起源来自于供应链管理一个产品生产过程分为三个阶段:原材料 - 中间产品 - 成产品。由于技术进步需求升级,生产过程从以前的企业内分工,转变为企业间分工。那么整个过程演变了如今的供应链管理流程&a…...

Qt之实现支持多选的QCombobox
一.效果 1.点击下拉列表的复选框区域 2.点击下拉列表的非复选框区域 二.实现 QHCustomComboBox.h #ifndef QHCUSTOMCOMBOBOX_H #define QHCUSTOMCOMBOBOX_H#include <QLineEdit> #include <QListWidget> #include <QCheckBox> #include <QComboBox>…...
【UI设计】Figma_“全面”快捷键
目录 1.快捷键与键位(mac与windows)2.基础快捷键3.操作区快捷键3.1视图3.2文字3.3选项3.4图层3.5组件 4.特殊技巧 Figma 是一个 基于浏览器 的协作式 UI 设计工具。【https://www.figma.com/】 Figma Sketch(UI 设计) InVision&a…...

计算机网络(谢希仁)第八版课后题答案(第一章)
1.计算机网络可以向用户提供哪些服务 连通性:计算机网络使上网用户之间可以交换信息,好像这些用户的计算机都可以彼此直接连通一样。 共享:指资源共享。可以是信息、软件,也可以是硬件共享。 2.试简述分组交换的要点 采用了存储转发技术。把报文(要发…...

argparse模块介绍
argparse是一个Python模块:命令行选项、参数和子命令解析器。argparse 模块可以让人轻松编写用户友好的命令行接口。程序定义了所需的参数,而 argparse 将找出如何从 sys.argv (命令行)中解析这些参数。argparse 模块还会自动生成…...
分布式、集群、微服务
分布式是以缩短单个任务的执行时间来提升效率的;而集群则是通过提高单位时间内执行的任务数来提升效率。 分布式是指将不同的业务分布在不同的地方。 集群指的是将几台服务器集中在一起,实现同一业务。 分布式中的每一个节点,都可以做集群…...
Android Studio的debug和release模式及签名配置
Android Studio的两种模式及签名配置 使用Android Studio 运行我们的app,无非两种模式:debug和release模式。 https://www.cnblogs.com/details-666/p/keystore.html...

【深蓝学院】手写VIO第8章--相机与IMU时间戳同步--笔记
0. 内容 1. 时间戳同步问题及意义 时间戳同步的原因:如果不同步,由于IMU频率高,可能由于时间戳不同步而导致在两帧camera之间的时间内用多了或者用少了IMU的数据,且时间不同步会导致我们首尾camera和IMU数据时间不同,…...

【Java集合类面试二十一】、请介绍TreeMap的底层原理
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:请介绍TreeMap的底层原理…...
Go语言Channel
在本教程中,我们将讨论Channel以及 Goroutines 如何使用Channel进行通信。 什么是Channel Channel可以被认为是 Goroutine 用来进行通信的管道。与水在管道中从一端流向另一端的方式类似,可以使用Channel从一端发送数据并从另一端接收数据。 声明Chan…...
java 编译 引用 jar 包进行编译和执行编译后的class文件
编译java文件 javac -encoding UTF-8 -Djava.ext.dirs./ -d . ./FtpTest.java 执行编译class文件 java -Djava.ext.dirs./ com.util.FtpTest com.util为包路径...

Linux系统之部署Tale个人博客系统
Linux系统之部署Tale个人博客系统 一、Tale介绍1.1 Tale简介1.2 Tale特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、部署Tale个人博客系统4.1 下载Tale源码4.2 查看Tale源码目录4.3 查看安装脚本内…...

【跟小嘉学 Rust 编程】三十三、Rust的Web开发框架之一: Actix-Web的基础
系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...
算法通关村|黄金挑战|K个一组进行反转
K个一组进行反转 1.头插法 public ListNode reverseKGroup(ListNode head, int k) {ListNode dummyNode new ListNode(0);dummyNode.next head;ListNode cur head;// 计算链表长度int len 0;while (cur ! null) {len;cur cur.next;}// 计算有几组int n len / k;ListNod…...

【Android Studio】工程中文件Annotate with Git Blame 不能点击
问题描述 工程文件中想要查看代码提交信息但是相关按钮不可点击 解决方法 Android Studio -> Preferences -> Version Control-> 在Unregistered roots里找到你想要的工程文件 点击左上角➕号 然后右下角Apply即可...
Ant Design Vue
2222222222222...

ATA-P2010压电叠堆功率放大器-直流偏置对压电叠堆测试的重要性
随着科技的发展和应用领域的扩展,压电技术在许多领域中得到了广泛的应用。在压电器件的研究和开发过程中,压电叠堆测试是非常重要的一环。本文通过对功率放大器的直流偏置功能在压电叠堆测试中的应用进行了深入研究,探讨了功率放大器直流偏置…...

短视频矩阵系统搭建/源头----源码
一、智能剪辑、矩阵分发、无人直播、爆款文案于一体独立应用开发 抖去推----主要针对本地生活的----移动端(小程序软件系统,目前是全国源头独立开发),开发功能大拆解分享,功能大拆解: 7大模型剪辑法(数学阶乘ÿ…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...

Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...