当前位置: 首页 > news >正文

C/C++【内存管理】

✨个人主页: Yohifo
🎉所属专栏: C++修行之路
🎊每篇一句: 图片来源

  • Love is a choice. It is a conscious commitment. It is something you choose to make work every day with a person who has chosen the same thing.

    • 爱是一种选择。这是一种有意识的承诺。这是你选择每天与一个选择同样事情的人一起工作的东西。

    配图


文章目录

  • 📘前言
  • 📘正文
    • 📖内存分布
      • 🖋️五大分区
      • 🖋️图解
    • 📖重温
      • 🖋️malloc/calloc/realloc
      • 🖋️free
    • 📖初识
      • 🖋️new
      • 🖋️delete
      • 🖋️特点
    • 📖探究
      • 🖋️封装实现
      • 🖋️代码展示
    • 📖new/delete 实现步骤
      • 🖋️内置类型
      • 🖋️自定义类型
    • 📖定位new
      • 🖋️应用场景
    • 📖注意事项
  • 📘总结


📘前言

C++中的内存管理机制和C语言是一样的,但在具体内存管理函数上,C语言malloc已经无法满足C++面向对象销毁的需求,于是祖师爷在C++中新增了一系列内存管理函数,即 newdelete
著名段子:如果你还没没有对象,那就尝试 new 一个吧

致敬C语言


📘正文

将内存分成不同区域是为了实现更好的管理,比如在我们现实生活中,一幢房子会被分为客厅、厨房、卧室、卫生间等区域,目的是为了使我们生活更加方便、空间利用更加合理,计算机也是如此,更何况是空间非常珍贵的内存,因此在我们的程序中存在不同内存分区

房子分区

📖内存分布

在程序中存在五大分区,各个分区各司其职,比如我们耳熟能详的栈区、堆区、静态区

🖋️五大分区

栈区:

  • 又称做堆栈,用于存储非静态局部变量、函数参数、返回值等,的空间是向下增长的

内存映射段:

  • 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信

堆区:

  • 用于程序运行时动态内存分配,堆是可以上增长的,我们的动态内存就是在上申请的

数据段:

  • 数据段中负责存储全局数据和静态数据

代码段:

  • 代码段中存储可执行的代码/只读常量

注意: 内存中还存在内核空间,但我们普通用户代码无法读写

🖋️图解

通过具体代码展示具体分布如下

图示

📖重温

先简单回顾下C语言中的动态内存管理

🖋️malloc/calloc/realloc

C语言提供了三种动态内存管理函数

malloc:申请指定大小的空间

int* pi = (int*)malloc(sizeof(int) * 1);	//申请一个整型
double* pd = (double*)malloc(sizeof(double) * 2);	//申请两个浮点型
char* pc = (char*)malloc(sizeof(char) * 3);	//申请三个字符型

注意: malloc申请的空间都是未初始化的,即被编译器置为随机值

calloc:将申请的空间初始化为 0

int* pi = (int*)calloc(1, sizeof(int));	//申请一个整型
double* pd = (double*)calloc(2, sizeof(double));	//申请两个个浮点型
char* pc = (char*)calloc(3, sizeof(char));	//申请三个字符型

注意: calloc参数列表与malloc不同,同时calloc申请的空间会被初始化为 0

realloc:对已申请的空间进行扩容

int* tmp = (int*)realloc(pi, sizeof(int) * 10);	//将 pi 扩容为十个整型
pi = tmp;	//常规使用方法

注意: 我们要对所有的申请函数进行空指针检查,预防野指针问题

堆区的空间由我们管理,编译器很信任我们,因此我们要做到有借有还,再借不难

凡是动态开辟的空间,用完后都需要释放

🖋️free

C语言提供的空间释放函数是free

free(tmp);	//此时tmp指向pi扩容后的空间,释放tmp就行了
tmp = pi = NULL;	//两者都需要置空
free(pd);
pd = NULL;
free(pc);	//只要是动态开辟的,都需要通过 free 释放
pc = NULL;

注意: 只有动态开辟的空间才能使用 free,同时一块空间不能释放两次。我们在 free 后通常会把指针置空

关于C语言动态内存管理更多细节可以看看这篇文章:《C语言动态管理》
这里就不再阐述

C语言 中管理函数只能对内置类型使用,而 C++ 中存在很多自定义类型,常规 malloc 等函数无能为力

📖初识

出现了新的关键字:newdelete,它们也有很多形式和使用细节

🖋️new

使用:

int* pi = new int;	//申请一个整型
double* pd = new double(3.14);	//申请一个浮点型并初始化为 3.14
char* pc = new char[5] {'H', 'e', 'l', 'l', 'o'};	//申请五个字符型,并分别初始化为 Helloc

注意:

  • newmalloc等不同,不需要进行空指针检查,也不需要进行类型转换
  • new 的使用极其简单

特点:

  • new可以用于自定义类型
  • 动态开辟时,会调用自定义类型的构造函数
//假设存在日期类
Date* ptr = new Date[5];	//申请五个日期类,这些类都在堆上

下面来看看C++中的内存释放函数

🖋️delete

形式:

  • delete 指针
  • delete[] 指针

使用:

  • C语言中的 free 可以用于释放所有动态申请函数,而 C++ 不行,申请与释放需要配套使用
int* pi = new int;
delete pi;	//直接释放double* pd = new double[5];
delete[] pd;	//释放五次Date* ptr = new Date[5];
delete[] ptr;	//释放五次,即调用五次日期类的析构函数

注意:

  • 需要配套使用,new int 搭配 delete,而 new int[] 需要搭配 delete[]

特点:

  • delete可以用于自定义类型
  • 调用销毁时,会先调用自定义类型的析构函数

🖋️特点

C语言C++动态内存管理函数的最大区别是: 是否会调用自定义类型的构造函数和析构函数

C语言明显不会,毕竟那时候还没有这些概念,而 C++ 作为面向对象的语言,调用构造与析构函数是必然的

C语言中的申请函数不能通过C++的释放函数进行释放,同理C++的申请空间也不能通过C语言的释放函数进行释放,比如下面这些情况是不合理的,可能引发问题

int* cPi = (int*)malloc(sizeof(int));
delete cPi;	//不合理的操作int* cppPi = new int;
free(cppPi);	//这样也是不合理的Date* ptr = new Date;
free(ptr);	//此时会报错,因为 free 并不会调用析构函数

切记,申请与释放要配套使用

📖探究

为何C++中的动态内存管理函数能做到调用构造析构函数呢?

  • 这是因为我们也是调用的其他函数,正是得益于C++中的封装

🖋️封装实现

newdelete 是用户进行动态内存申请和释放的 操作符,它们在实现时会去调用真正的全局函数 operator newoperator delete,具体调用情况如下所示:

newnew []

  • new 调用 operator new
  • new [] 调用 operator new[]

deletedelete []

  • delete 调用 operator delete
  • delete [] 调用 operator delete[]

注意: operator new[] 最终是调用 operator newoperator delete[] 最终也是调用 operator delete
所以严格来说,operator newoperator delete 才是我们探讨的主角

🖋️代码展示

先来看看 operator new 的代码实现

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECLoperatornew(size_tsize)_THROW1(_STDbad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory//如果申请内存失败了,这里会抛出bad_alloc类型异常staticconststd::bad_allocnomem;_RAISE(nomem);}return(p);
}
//代码源自:比特教育科技 https://www.bitejiuyeke.com/index

可以看到,其实 operator new 就是通过对 malloc 的封装实现的,不过进行了改进,当对象为自定义类型时,会去调用它的构造函数,并且当开辟失败时,会抛出异常

再来看看 operator delete 的代码实现

/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK); /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK); /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
//代码源自:比特教育科技 https://www.bitejiuyeke.com/index

operator delete 的代码中也有 free 的影子,当释放对象为自定义类型时,会调用它的析构函数

📖new/delete 实现步骤

下面再来看看两者具体的实现步骤

🖋️内置类型

对于内置类型来说,使用 malloc/free 和 new/delete 没什么区别

🖋️自定义类型

对于自定义类型,new/delete 的实现步骤如下:

new

  • 调用 operator new 申请空间
  • 在申请的空间上调用构造函数

delete

  • 在空间上调用析构函数
  • 再调用 operator delete 释放空间

new []

  • 调用 operator new[] 函数,根据数值N,调用N次 operator new 函数申请空间
  • 在申请的空间上调用N次构造函数

delete []

  • 在申请的空间上调用N次析构函数
  • 调用 operator delete[] 函数,然后由函数再调用 operator delete 释放空间

📖定位new

定位newnew 的新用法

目的:

  • 对已开辟而未初始化的空间进行初始化

形式:

  • new(指针)构造函数
//定位new
Stack* ptr = (Stack*)malloc(sizeof(Stack));	//malloc 不会调用构造函数,此时未初始化new(ptr)Stack();	//通过定位new初始化对象

🖋️应用场景

定位new 可以用在内存池这个项目中

  • 向堆中申请一块定额空间,此时空间未初始化
  • 此时就需要通过 定位new 来进行初始化
    内存池

📖注意事项

开辟与释放需要配对使用

malloc/calloc/realloc 搭配 free

new 搭配 delete

new [] 搭配 delete []


📘总结

以上就是关于 C++ 内存管理的全部内容了,记住一点就够了:认识 new ,配对使用。

如果你觉得本文写的还不错的话,可以留下一个小小的赞👍,你的支持是我分享的最大动力!

如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


星辰大海

相关文章推荐
类和对象实操
类和对象实操之【日期类】

===============

类和对象合集系列
类和对象(下)
类和对象(中)
类和对象(上)

===============

C++入门必备
C++入门基础

感谢支持

相关文章:

C/C++【内存管理】

✨个人主页: Yohifo 🎉所属专栏: C修行之路 🎊每篇一句: 图片来源 Love is a choice. It is a conscious commitment. It is something you choose to make work every day with a person who has chosen the same thi…...

第8篇:Java编程语言的8大优势

目录 1、简单性 2、面向对象 3、编译解释性 4、稳健性 5、安全性 6、跨平台性...

STM32定时器实现红外接收与解码

1.NEC协议 红外遥控是一种比较常用的通讯方式,目前红外遥控的编码方式中,应用比较广泛的是NEC协议。NEC协议的特点如下: 载波频率为 38KHz8位地址和 8位指令长度地址和命令2次传输(确保可靠性)PWM 脉冲位置调制&#…...

18- Adaboost梯度提升树 (集成算法) (算法)

Adaboost 梯度提升树: from sklearn.ensemble import AdaBoostClassifier model AdaBoostClassifier(n_estimators500) model.fit(X_train,y_train) 1、Adaboost算法介绍 1.1、算法引出 AI 39年(公元1995年),扁鹊成立了一家专治某疑难杂症…...

zlink 介绍

zlink 是一个基于 flink 开发的分布式数据开发工具,提供简单的易用的操作界面,降低用户学习 flink 的成本,缩短任务配置时间,避免配置过程中出现错误。用户可以通过拖拉拽的方式实现数据的实时同步,支持多数据源之间的…...

C++之std::string的resize与reverse

std::string的resize与reverse前言1.resize2.reserve前言 在C中我们经常用std::string 来保存字符串,其中有两个比较常用但是却平时容易被搞混的两个函数,分别是resize和reserve,模糊意识里,这两个方法都是对std::string的容量或元…...

在.net中运用ffmpeg 操作视频

using System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.Text;namespace learun.util{/// <summary>/// ffmpeg视频相关处理的类/// </summary>public class FFmpegUtil{public static int Run(string cmd){try{//…...

05- 线性回归算法 (LinearRegression) (算法)

线性回归算法(LinearRegression)就是假定一个数据集合预测值与实际值存在一定的误差, 然后假定所有的这些误差值符合正太分布, 通过方程求这个正太分布的最小均值和方差来还原原数据集合的斜率和截距。当误差值无限接近于0时, 预测值与实际值一致, 就变成了求误差的极小值。 fr…...

JAVA补充知识01之枚举enum

目录 1. 枚举类的使用 1.1 枚举类的理解 1.2 举例 1.3 开发中的建议&#xff1a; 1.4 Enum中的常用方法 1.5 熟悉Enum类中常用的方法 1.6 枚举类实现接口的操作 1.7 jdk5.0之前定义枚举类的方式 &#xff08;了解即可&#xff09; 1.8 jdk5.0之后定义枚举类的方式 1…...

jenkins下配置maven

1. 先在jenkins服务器上安装maven 下载-解压-重命名-启动 [rootVM-0-12-centos local]# wget https://mirrors.aliyun.com/apache/maven/maven-3/3.9.0/binaries/apache-maven-3.9.0-bin.tar.gz [rootVM-0-12-centos local]# tar xf apache-maven-3.9.0-bin.tar.gz [rootVM-0…...

春季开学即将到来!大学生活必备数码清单奉上

马上就要开学了&#xff0c;你的返校装备是否已经准备齐全了呢&#xff1f;对于高校学生来说&#xff0c;很多数码产品都属于必备装备&#xff0c;比如下面这几款产品就受到了大量年轻消费者的喜爱&#xff0c;在它们的帮助下能够让大家的学习时光变得更快乐。1、不入耳黑科技骨…...

ubuntu18.04 天选2 R95900hx 3060显卡驱动安装

天选2 R95900hx 3060显卡驱动安装需求问题解决内核集显显卡驱动需求 外接显示器&#xff0c;安装nvidia驱动 问题 由于一开始直接在软件和更新中附加读懂安装了nvidia-470&#xff0c;导致系统黑屏。 解决 grub页面系统选择进入ubuntu recovery模式&#xff0c;选择root&a…...

Harbor安装部署实战详细手册

文章目录前言一、安装docker二、安装docker-compose1.下载2.赋权3.测试三、安装harbor1.下载2.解压3.修改配置文件4.部署5.配置开机自启动6.登录验证7.补充说明四、harbor使用问题1.docker login问题&#xff1a;Error response from daemon: Get https://: http: server gave …...

华为OD机试真题JAVA实现【箱子之形摆放】真题+解题思路+代码(20222023)

🔥系列专栏 华为OD机试(JAVA)真题目录汇总华为OD机试(Python)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出描述示例一输入输出说明备注解题思路Code运行结果版权说明...

华为OD机试 - 事件推送(Python)| 真题+思路+考点+代码+岗位

事件推送 题目 同一个数轴 X 上有两个点的集合 A={A1, A2, …, Am} 和 B={B1, B2, …, Bn}, Ai 和 Bj 均为正整数,A、B 已经按照从小到大排好序,A、B 均不为空, 给定一个距离 R (正整数), 列出同时满足如下条件的所有(Ai, Bj)数对: Ai <= BjAi, Bj 之间的距离小于…...

【Linux】信号量

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…...

android-java同步方法和异步方法

接口 Java接口是一系列方法的声明&#xff0c;是一些方法特征的集合&#xff0c;一个接口只有方法的特征没有方法的实现&#xff0c;因此这些方法可以在不同的地方被不同的类实现&#xff0c;而这些实现可以具有不同的行为&#xff08;功能&#xff09;。 两种含义&#xff1a…...

Flask入门(5):请求和响应

目录5.请求和响应5.1 请求5.2 响应5.请求和响应 5.1 请求 request对象封装解析了请求报文中的数据&#xff0c;其大部分功能是由依赖包werkzeug完成的&#xff0c;并且每个request对象都是线程隔离的&#xff0c;保证了数据的安全性。 request对象的属性 1.request.method …...

记进组后第五次组会汇报

2023年2月14日 日记一、小组组会二、实验室组会1、汇报内容&#xff08;1&#xff09;参考文献&#xff08;2&#xff09;CQF机制a.研究现状b.相关思考&#xff08;3&#xff09;研究计划2、汇报反馈一、小组组会 上午十点整&#xff0c;小组组会开始&#xff0c;有两个同学我…...

nil Foundation的Placeholder证明系统(2)

前序博客&#xff1a; nil Foundation的Placeholder证明系统&#xff08;1&#xff09; nil; Foundation团队2022年11月论文《Placeholder证明系统》。[2022年11月29日版本] 8. 优化 8.1 Batched FRI 不同于单独检查每个commitment&#xff0c;可对其进行FRI聚合。如对多项…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...