C和C++内存管理
C和C++内存管理
- (一)C/C++内存分布
- (二)C语言动态内存管理
- (三)c++内存管理
- (3.1)new/delete操作内置类型
- (3.2)new和delete操作自定义类型
- (四)operator new与operator delete函数
- (五)new与delete的实现原理
- (5.1)内置类型
- (5.2)自定义类型
- (5.3)不匹配使用(面试可能会考)
- (六)定位new表达式(placement-new) (了解)
- (七)malloc/free和new/delete的区别(重点)
(一)C/C++内存分布
我们可以在任务管理器中查看电脑当前进程:
下图是在c语言阶段我们学过的内存区域划分,这一块在C++同样适用:
说明:
- 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
- 堆用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段–存储全局数据和静态数据。
- 代码段–可执行的代码/只读常量。
那我们先根据几个题目来回忆我们c语言中内存如何分配的:
前五个比较简单,大家一定不能出错
第一个:A,main函数外定义的变量都是静态的,生命周期都是从程序开始到结束
第二个:A,全局静态一样
第三个:A,由static修饰的也是全局的
第四个:A,没有static修饰的都是局部的
第五个:A,这里的数组名代表整个数组,也是在局部的栈里存放
后面六个难度上升了很多:
第六个:A,char2也是数组,并且我是我们主动申请的,所以也在栈
第七个:A,*char2是解引用,这里的char2代表的首元素地址,首元素a在栈上
第八个:A, pchar3是一个指针,不管它的const修饰指向的内容还是指针,指针都存于栈
第九个:D,*pchar3也是指向第一个元素的地址,但是内容部分用const修饰了,所以在常量区
第十个:A,ptr1是一个指针
第十一个:B,ptr1所指向的内容使我们主动开辟的,所以在堆区。
(二)C语言动态内存管理
C语言中动态内存管理方式:malloc/calloc/realloc/free
这一块在c语言部分讲的很多了,所以我就简单的带大家回忆一下,
malloc:开固定的空间,不初始化。
calloc:开空间,初始化0.
realloc:支持拓展我们原来开的空间。
free:释放我们主动开辟的空间。
【面试题】
- malloc/calloc/realloc的区别?
- malloc的实现原理? 视频链接:https://www.bilibili.com/video/BV117411w7o2/?spm_id_from=333.788.videocard.0
这一块就这样过了,如果这一块还不懂得,建议回去补一下c语言的课。
(三)c++内存管理
(3.1)new/delete操作内置类型
我们简单得来使用一下new和delete
int main()
{int* p1 = new int;int* p2 = new int[10];//简化了c语言sizeof与强转部分delete p1;delete[] p2;//初始化int* p3 = new int(3);int* p4 = new int[10] {0};//全都初始化为0int* p5 = new int[10] {1, 2, 3, 4};//前四个值初始化delete p3;delete[] p4;delete[] p5;return 0;
}
(3.2)new和delete操作自定义类型
class A
{
public:A(int a = 0){_a = a;cout << "A()" << endl;}~A(){cout << "~A()" << endl;}void print(){cout << _a << endl;}
private:int _a;
};int main()
{A* p1 = new A;A* p2 = new A(1);delete p1;delete p2;return 0;
}
大家可以看到c++这块new与delete会自动调用自定义类型的构造和析构函数。
这一块非常的方便我们来演示一下c++里的链表:
struct ListNode
{int _val;ListNode* next;ListNode(int val) :_val(val),next(nullptr){}
};
//c++的struct里的变量与函数均为共有的,这一部分前面讲过
int main()
{ListNode* n1 = new ListNode(1);ListNode* n2 = new ListNode(2);ListNode* n3 = new ListNode(3);ListNode* n4 = new ListNode(4);ListNode* n5 = new ListNode(5);n1->next = n2;n2->next = n3;n3->next = n4;n4->next = n5;return 0;
}
这里是不是相较于c里的初始化啊,插入什么的简单许多。
总之我们只需要记住在c++的使用过程中使用new,delete就行,因为new包含了malloc,delete包含了free。
此外我们在使用new和时是不是与malloc还有一个差别,那就是没有判断是否申请成功,C++引入了抛异常。
抛异常在c++进阶里才会详细的讲解,这里我们简单的演示一下:
int main()
{try{throw try catchvoid* p1 = new char[1024 * 1024 * 1024];cout << "p1" << endl;void* p2 = new char[1024 * 1024 * 1024];cout << "p2" << endl;void* p3 = new char[1024 * 1024 * 1024];cout << "p3" << endl;}catch (const exception& e){cout << e.what() << endl;//发生了什么错误}return 0;
}void func()
{//throw try catchvoid* p1 = new char[1024 * 1024 * 1024];cout << "p1" << endl;void* p2 = new char[1024 * 1024 * 1024];cout << "p2" << endl;void* p3 = new char[1024 * 1024 * 1024];cout << "p3" << endl;
}int main()
{try{func();}catch (const exception& e){cout << e.what() << endl;//发生了什么错误}return 0;
}
上面是在main函数里申请失败,下面是在调用的一个函数里失败。
void func()
{int n = 0;while (1){char* p1 = new char[1024 * 1024];//1024*1024byte是1Mcout << "p1->" << endl;n++;cout << n << endl;;}
}int main()
{try{func();}catch (const exception& e){cout << e.what() << endl;//发生了什么错误}return 0;
}
这个函数就是来看在32位环境下究竟能申请多少MB
大家可以看到32位(总共4G)下大概只能申请1.7G左右,所以堆在内存占大部分。
(四)operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
operator new 与operator delete底层源码如下,这一块大家不需要看的特别明白,
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)//mallocif (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}
/*
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)//free
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
(五)new与delete的实现原理
(5.1)内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
(5.2)自定义类型
(1)new的原理
- 调用operator new函数申请空间
- 在申请的空间上执行构造函数,完成对象的构造
(2)delete的原理 - 在空间上执行析构函数,完成对象中资源的清理工作
- 调用operator delete函数释放对象的空间
(3)new T[N]的原理 - 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
- 在申请的空间上执行N次构造函数
(4)delete[]的原理 - 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
- 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
(5.3)不匹配使用(面试可能会考)
上面第一组是可以的,第二组不行,我们可以清楚的看到第二组的free少调用了析构函数,这里有内存泄漏的风险。
那我们来看一个神奇的东西:
class A
{
public:A(int a = 0, int b = 0){_a = a;_b = 1;cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;int _b;
};
class B
{
private:int _a;int _b;
};
int main()
{int* p1 = new int[10];//这里没有调用构造函数,实质上只有mallocdelete p1;//这里虽然不匹配,但是内存也不会出现泄漏,实质这里只使用freefree(p1);A* p2 = new A[10];delete p2;//这里有问题B* p3 = new B[10];delete p3;//这里没有问题return 0;
}
大家自己可以去vs里运行一下,那为什么A有问题,B没有问题呢?
首先我们的A与B大小是不是都是8个字节,那我们都调用了10次,那么10个这样的对象是不是80个字节,但是实际上A的10个对象占了84个字节,B的10个对象占了80字节,多的4个字节是用来存储对象的个数。
那为什么A会多开4个字节存储个数呢?
这是因为B没有析构函数,所以编译器就给优化了,严格的来说多存储的个数是给delete后面的[]使用的,当类没有析构函数时,就认为没有开辟的空间需要释放,只要我们写了析构,编译器就不会再有优化,那B的大小就变为84,。
这里总结就是一定要匹配使用,不要错配。
(六)定位new表达式(placement-new) (了解)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
上面什么意思呢?
就是我们可以只开空间,不初始化,也就是malloc的功能。
class A
{
public:A(int a = 0, int b = 0){_a = a;_b = 1;cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;int _b;
};
int main()
{A* ptr1 = new A;//调用了构造A* ptr2 = (A*)operator new(sizeof(A));//不调构造new(ptr2)A(1, 0);//定位new//下面两步合起来等于上面一步delete ptr1;ptr2->~A();//析构函数是可以显示调用的operator delete(ptr2);//下面两步合起来等于上面一步return 0;
}
大家可能认为这有点脱了裤子放屁对吧,但我想说的是—说得对,但它也有他自己的用途,在生活中99%的场景用不到,需要用的场景是内存池、线程池,连接池,这一块不讲,只是给大家说下有这个东西。
(七)malloc/free和new/delete的区别(重点)
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放
相关文章:

C和C++内存管理
C和C内存管理 (一)C/C内存分布(二)C语言动态内存管理(三)c内存管理(3.1)new/delete操作内置类型(3.2)new和delete操作自定义类型 (四)…...
axios取消请求
1.使用CancelToken: class RequestHttp {service: AxiosInstance;public constructor(config: AxiosRequestConfig) {// 实例化axiosthis.service axios.create(config);/*** description 请求拦截器* 客户端发送请求 -> [请求拦截器] -> 服务器*/this.service.interce…...
阿里中间件——diamond
一、前言 最近工作不忙闲来无事,仔细分析了公司整个项目架构,发现用到了很多阿里巴巴集团开源的框架,今天要介绍的是中间件diamond. 二、diamond学习笔记 1、diamond简介 diamond是一个管理持久配置(持久配置是指配置数据会持久化…...
pyenv -- 一款macos下开源的多版本python环境安装管理工具 国内加速版安装 + 项目venv虚拟环境 pip加速 使用与总结
一个比较方便实用的python多版本环境安装管理工具, 阿里云加速版本 pyenv安装方法: 直接克隆本下面到你的本地目录,然后设置环境变量即可 git clone https://gitee.com/tekintian/pyenv.git ~/.pyenv 环境变量配置 在~/.bash_profile 或者 .zshrc 中增加环境变量 export …...
VitePress 自定义 CSS 指南
VitePress 是一款基于 Vite 和 Vue 3 的静态网站生成器,专为文档编写而设计。尽管 VitePress 提供了丰富的默认主题,但在某些情况下,我们可能需要对其进行更深入的定制以满足特定的视觉需求。本文将详细介绍如何通过覆盖根级别的 CSS 变量来自…...

【舍入,取整,取小数,取余数丨Excel 函数】
数学函数 1、Round函数 Roundup函数 Rounddown函数 取整:(Int /Trunc)其他舍入函数: 2、Mod函数用Mod函数提取小数用Mod函数 分奇偶通过身份证号码判断性别 1、Round函数 Roundup函数 Rounddown函数 Round(数字,保留几位小数)(四…...

无线信道中ph和ph^2的场景
使用 p h ph ph的情况: Rayleigh 分布的随机变量可以通过两个独立且相同分布的零均值、高斯分布的随机变量表示。设两个高斯随机变量为 X ∼ N ( 0 , σ 2 ) X \sim \mathcal{N}(0, \sigma^2) X∼N(0,σ2)和 Y ∼ N ( 0 , σ 2 ) Y \sim \mathcal{N}(0, \sigma^2)…...

HCIA--实验五:静态路由综合实验
静态路由综合实验 一、实验内容: 1.需求/目的: 在ensp模拟器中使用四个路由器,并且在路由器上创建loopback接口,相当于连接了一台主机,通过配置静态路由的方式实现全网通。 二、实验过程 1.道具: 4个…...

不同vlan之间的通信方法
1.通过路由器的物理接口 1.给PC1,PC2配置IP地址,网关2.进入交换机配置vlan,交换机所有口都配置access口并绑定vlan3.配置路由器,进入路由器的两个接口配置网关IP和掩码缺点:成本高,每增加一个vlan就需要一个物理端口和…...
java后端框架
框架就是对技术的封装。 本篇博客小博主首先对以后我们要学习的框架进行简单概述,使大家对框架有一定的基本概念。 一.mybatis mybatis就是对jdbc(数据库连接)进行封装,避免了jdbc中手动设置参数,手动映射结果的操作。…...

如何在Word中插入复选框
如何在Word中插入复选框:详细教程与技巧 在Word中插入复选框是一项非常实用的技巧,尤其是在制作问卷调查、待办事项清单、交互式表单或文档中需要用户进行选择时,复选框不仅能提高文档的功能性,还能显得更加专业。本文将详细讲解…...
Android 源码中jni项目 加载so目录小结
Android 源码中jni项目 加载so目录小结 文章目录 Android 源码中jni项目 加载so目录小结一、前言二、so目录验证测试1、jni so文件错误报错(1)报错1 - 未找到so文件:(2)报错2 - so文件中未找到native方法: …...

24/9/6算法笔记 kaggle 房屋价格
预测模型主要分为两大类: 回归模型:当你的目标变量是连续的数值时,你会使用回归模型进行预测。回归模型试图找到输入特征和连续输出之间的关联。一些常见的回归模型包括: 线性回归(Linear Regression)岭回归…...

【MA35D1】buildroot 编译使用经验
文章目录 芯片介绍Buildroot开发Linux实践环境搭建代码获取编译执行步骤(仅适用于我公司产品) 后续有需要更改的输出文件目录 芯片介绍 NuMicro MA35D1系列为一颗异核同构的多核心微处理器,适用于高端 Edge IIoT Gateway。它是基于双核 64 位…...

排查 MyBatis XML 配置中的 IF 语句与传值名称不匹配的 Bug
文章目录 本文档只是为了留档方便以后工作运维,或者给同事分享文档内容比较简陋命令也不是特别全,不适合小白观看,如有不懂可以私信,上班期间都是在得 前言,在改一个bug得时候发现一个有意思得问题,就是myb…...

数字证书与公钥基础设施
关注这个证书的其他相关笔记:NISP 一级 —— 考证笔记合集-CSDN博客 0x01:数字证书 数字证书是由第三方可信机构(一般是证书服务器)颁发的数字证书,可以证明身份的可信度。 数字证书具有以下特点以及性质:…...
拥抱数智化,JNPF低代码平台如何推动企业转型升级
随着信息技术的飞速发展,企业面临的市场竞争日益激烈,传统的业务流程和管理模式已经难以满足快速变化的市场需求。数智化转型成为企业持续发展的必由之路。在这一过程中,低代码开发平台扮演了至关重要的角色。本文将探讨JNPF低代码平台如何助…...

Linux shell脚本 (十二)case语句_linux awk case语句
case工作方式如上所示。取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后。 取值将…...

【二等奖成品论文】2024年数学建模国赛B题25页成品论文+完整matlab代码、python代码等(后续会更新)
您的点赞收藏是我继续更新的最大动力! 一定要点击如下的卡片,那是获取资料的入口! 【全网最全】2024年数学建模国赛B题31页完整建模过程25页成品论文matlab/python代码等(后续会更新「首先来看看目前已有的资料,还会…...
国内快速高效下载 HuggingFace上的各种大语言模型
预先安装: apt install aria2 # sudo apt install aria2apt install git-lfs # sudo apt install git-lfs下载hfd wget https://hf-mirror.com/hfd/hfd.shchmod ax hfd.sh设置环境变量 Linux export HF_ENDPOINThttps://hf-mirror.comWindows $env:HF_ENDPOINT…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...