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…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑 在电子商务领域,转化率与网站性能是决定商业成败的核心指标。今天,我们将深入解析不同类型电商平台的转化率基准,探讨页面加载速度对用户行为的…...
SE(Secure Element)加密芯片与MCU协同工作的典型流程
以下是SE(Secure Element)加密芯片与MCU协同工作的典型流程,综合安全认证、数据保护及防篡改机制: 一、基础认证流程(参数保护方案) 密钥预置 SE芯片与MCU分别预置相同的3DES密钥(Key1、Key2…...