【C++】————内存管理

news2024/11/17 3:57:37

 9efbcbc3d25747719da38c01b3fa9b4f.gif

                                                      作者主页:     作者主页

                                                      本篇博客专栏:C++

                                                      创作时间 :2024年6月26日

9efbcbc3d25747719da38c01b3fa9b4f.gif

一、C++内存分布

我们先来看一串代码:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
	static int staticVar = 1;
	int localVar = 1;
	int num1[10] = { 1, 2, 3, 4 };
	char char2[] = "abcd";
	const char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
	free(ptr1);
	free(ptr3);
}

然后来回答一下下面这几个问题:

1. 选择题:
选项: A.栈 B.堆 C.数据段 D.代码段
globalVar在哪里?__C__ staticGlobalVar在哪里?__C__
staticVar在哪里?__C__ localVar在哪里?__A__
num1 在哪里?__A__
char2在哪里?__A__ *char2在哪里?__A__
pChar3在哪里?__A__ *pChar3在哪里?__D__
ptr1在哪里?__A__ *ptr1在哪里?__B__
 
2. 填空题:
sizeof(num1) = __40__;
sizeof(char2) = __5__; strlen(char2) = __4__;
sizeof(pChar3) = __4/8__; strlen(pChar3) = __4__;
sizeof(ptr1) = __4/8__;

答案我们已经给出,这里我们给出一幅图来讲解一下这是为什么?(图最后的两个画反了,数据段和代码段应该调换一下)

这就是上面每个答案的原因,不懂的话可以看一下。

下面我们再来讲一下关于C++中几个内存区的概念:

  • 栈区:在执行我们的自定义函数时,内部定义的变量都可以在栈区上创建,函数执行结束时这些变量被自动释放栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  • 堆区:一般由程序员进行相应的分配和释放,若程序员不进行释放,程序结束时可能自动收回,分配方式类似于链表。
  • 数据段(静态区):存放全局变量、静态数据、程序结束或由系统释放。
  • 代码段:存放函数体的二进制代码

二、C语言中动态内存分配的方式

关于malloc/calloc/realloc,我已经在之前的博客讲过,大家可以去看一下:c语言动态内存分配

演示代码:

void Test()
{
	int* p1 = (int*)malloc(sizeof(int));
	free(p1);
	int* p2 = (int*)calloc(4, sizeof(int));
	free(p2);
	int* p3 = (int*)realloc(p2, sizeof(int) * 10);
	free(p3);
}

malloc:
在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首地址

calloc:
与malloc相似,不过函数calloc() 会将所分配的内存空间中的每一位都初始化为零

realloc:
 给一个已经分配了地址的指针重新分配空间,可以做到对动态开辟内存大小的调整。

这里给出一个关于这三个的面试题:

【面试题】:malloc/calloc/realloc的区别?

  1. 函数malloc不能初始化所分配的内存空间,而函数calloc能.如果由malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之, 如果这部分内存曾经被分配过,则其中可能遗留有各种各样的数据.也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常进行,但经过一段时间(内存空间还已经被重新分配)可能会出现问题.
  2. 函数calloc() 会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;
  3. 函数malloc向系统申请分配指定size个字节的内存空间.返回类型是 void类型.void表示未确定类型的指针.C,C++规定,void* 类型可以强制转换为任何其它类型的指针.
  4. realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变.当然,对于缩小,则被缩小的那一部分的内容会丢失.realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址.相反,realloc返回的指针很可能指向一个新的地址.
  5. realloc是从堆上分配内存的.当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,此时即原地扩;如果数据后面的字节不够,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上.这句话传递的一个重要的信息就是数据可能被移动,即异地扩

三、C++内存管理方式

我们知道C++是兼容C的,但是吧,C的内存管理方式在C++中用起来总是感觉有点麻烦,所以C++就又提出了一种新的方式,就是new和delete。

new和delete操作内置类型

void Test()
{
	// new一个int类型的空间
	int* ptr4 = new int;
	// new一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
	// new10个int类型的空间
	int* ptr6 = new int[10];
	// new10个int类型的空间并初始化
	int* ptr7 = new int[10]{ 10,9,8,7,6,5 }; //跟数组的初始化很像,大括号有几个,初始化几个,其余为0。不过C++11才支持的语法
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
	delete[] ptr7;
}

这里我们要注意的就是关于new和delete的使用形式,申请和释放单个空间,就用new和delete,申请和释放连续的空间就用new[]和delete[].

总结:对于内置类型,使用malloc和new,没什么区别,区别就在于自定义类型。

new / delete 操作自定义类型

首先我先给出一个结论:

  • malloc为自定义类型开辟空间不会调用构造函数,而new会
  • delete会调用析构函数,而free不会

我们先看一下malloc和free:

再来看一下new和delete:

很明显,使用new,既可以开辟空间,又调用了构造函数从而完成初始化,而delete时调用了析构函数,以此释放空间。

在我们先前学习的链表中,C语言为了创建一个节点并将其初始化,需要单独封装一个函数进行初始化,我C++只需要用new即可开空间+初始化:

struct ListNode
{
	struct ListNode* _next;
	int _val;
    //构造函数
	ListNode(int val = 0) 
		:_next(nullptr)
		,_val(val)
	{}
};
int main()
{
	ListNode* n2 = new ListNode(10); //C++的new相当于我之前的BuyListNode函数
	return 0;
}

这里总结起来就是一句话:在申请自定义类型的空间的时候,new和delete会调用构造函数和析构函数,malloc和free不会

malloc和new申请内存空间失败处理情况的不同之处:

再就是关于malloc和new如果申请空间失败的话处理情况不同,malloc会返回空指针,而new会抛异常,我们就不需要手动去检查是否为空了。

看下面这串代码:

int main()
{
    //malloc失败,返回空指针
	int* p1 = (int*)malloc(sizeof(int) * 10);
	assert(p1); //malloc出来的p1需要检查合法性
    //new失败,抛异常
	int* p2 = new int;
	//new出来的p2不需要检查合法性
}

为了演示malloc和new在开辟内存时失败的场景,这里给出一份测试:

int main()
{
	void* p3 = malloc(1024 * 1024 * 1024); //1G
	cout << p3 << endl;
	void* p4 = new char[1024 * 1024 * 1024];
	cout << p4 << endl;
}

换个顺序试试:

此段测试充分说明了我先开辟1G的大小是没有问题的,但是再开辟1个G的大小就会报错了,为了能够看出malloc和new均报错的场景,我们再定义一个指针占据这1G:

此段测试更能够清楚的看出mallloc失败会返回空指针,而new失败会抛异常。 对于抛异常,我们理应进行捕获,不过这块内容我后续会讲到,这里先给个演示:

四、operator new和operator delete的讲解

关于new和delete,他们叫做被用户用来进行内存申请和释放的操作符,而operator new和operator delete是系统提供的全局函数。

new其实就是在底层调用operator new函数来申请空间,delete就是在底层调用operator delete来释放空间。

  • 具体使用operator new和operator delete的操作如下:
int main()
{
	Stack* ps2 = (Stack*)operator new(sizeof(Stack));
	operator delete(ps2);
 
	Stack* ps1 = (Stack*)malloc(sizeof(Stack));
    assert(ps1);
	free(ps1);
}

operator new和oparator delete其实和malloc和free一样,也不会去调用构造函数和析构函数。但是也有不同,那就是operator new申请失败不会返回空指针,而是抛异常。

  • operator new和operator delete的意义体现在new和delete的底层原理:
Stack* ps3 = new Stack;
new的底层原理:转换成调用operator new + 构造函数
delete ps3;
delete的底层原理:转换成调用operator delete + 析构函数

new和delete的实现原理:

内置类型:

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

自定义类型:

new的原理:

调用operator new函数申请空间
在申请的空间上执行构造函数,完成对象的构造
delete的原理:

在空间上执行析构函数,完成对象中资源的清理工作
调用operator delete函数释放对象的空间
new T[N]的原理:

调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
在申请的空间上执行N次构造函数
delete[ ]的原理:

在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

五、常见相关面试题

5.1、malloc/free和new/delete的区别:

共同点:都是在堆上申请空间,且需要用户手动释放

不同点:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new申请的会初始化
  3. malloc申请时需要手动计算空间大小并传递,new申请时只需要在其后跟上类型即可
  4. malloc返回类型为void*,使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请失败时,返回的是空指针,new申请失败时会抛异常(底层区别)
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理(底层区别)

5.2、内存泄漏:

什么是内存泄漏:

内存泄露是指因为疏忽或者错误导致未能及时释已经不再使用的空间,内存泄露并不是指这段空间物理意义上的消失,而是由于应用程序分配某一块内存之后,因为设计错误,对于该段内存失去了管理的权力,因为导致了内存的浪费。(内存泄漏也可以当作是指针丢了)

内存泄露的危害:

长期运行会导致内存泄漏,影响很大,如操作系统、后台服务、出现内存泄漏会导致相应越来越慢、最终卡死。

void MemoryLeaks()
{
	// 1.内存申请了忘记释放
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;
	// 2.异常安全问题
	int* p3 = new int[10];
	Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
	delete[] p3;
}

最后:

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.一个冷知识:
屏蔽力是一个人最顶级的能力,任何消耗你的人和事,多看一眼都是你的不对。

2.你不用变得很外向,内向挺好的,但需要你发言的时候,一定要勇敢。
正所谓:君子可内敛不可懦弱,面不公可起而论之。

3.成年人的世界,只筛选,不教育。

4.自律不是6点起床,7点准时学习,而是不管别人怎么说怎么看,你也会坚持去做,绝不打乱自己的节奏,是一种自我的恒心。

5.你开始炫耀自己,往往都是灾难的开始,就像老子在《道德经》里写到:光而不耀,静水流深。

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1868183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑怎么设置锁屏密码?这3个方法你知道吗

在日常生活中&#xff0c;电脑已成为我们工作和娱乐的重要工具。为了保护个人信息和数据安全&#xff0c;设置锁屏密码是必不可少的一步。通过设置锁屏密码&#xff0c;您可以有效防止未经授权的访问&#xff0c;确保电脑上的隐私和数据不被泄露。本文将详细介绍电脑怎么设置锁…

STM32_hal_STM32Cude_实现RT—Thread系统

1stm32cude下载系统 1.-2下载显示绿色的为下载成功 2为项目导入系统---点击如下选项 2-1选中如下 意思为 kemel 系统内核 shell shell的实现 device 设备 2-2可以看到项目选项中多了如图选项 3实现led闪烁 3-1 定义两个引脚用于控制led 3-2选择时钟源 3-3更改延迟函数…

Planned independent reguirement can only be maintained via the network

背景&#xff1a;用户上线ps系统&#xff0c;物料用策略70跑需求 但是因为通用料被改了策略&#xff0c;改成其他的了&#xff0c;影响到计划独立需求了。 如果用户不需要了哪个料就会把数量改为0&#xff0c;或者直接删掉物料。之前建议是改成0&#xff0c;这样还有个记录在…

【Python机器学习】交互特征与多项式特征

对于线性模型来说&#xff0c;想要丰富特征&#xff0c;还有一种方法是添加原始数据的交互特征和多项式特征。这种特征工程通常用于统计建模&#xff0c;但也经常用于实际的机器学习应用中。 交互特征 上一篇的例子里&#xff0c;线性模型对wave数据集的的每个箱子都学到一个…

基于稀疏矩阵方法的剪枝压缩模型方案总结

1.简介 1.1目的 在过去的一段时间里&#xff0c;对基于剪枝的模型压缩的算法进行了一系列的实现和实验&#xff0c;特别有引入的稀疏矩阵的方法实现了对模型大小的压缩&#xff0c;以及在部分环节中实现了模型前向算法的加速效果&#xff0c;但是总体上模型加速效果不理想。所…

从零到一打造自己的大模型:模型训练

前言 最近看了很多大模型&#xff0c;也使用了很多大模型。对于大模型理论似乎很了解&#xff0c;但是好像又缺点什么&#xff0c;思来想去决定自己动手实现一个 toy 级别的模型&#xff0c;在实践中加深对大语言模型的理解。 在这个系列的文章中&#xff0c;我将通过亲手实践…

【面试题】Spring面试题

目录 Spring Framework 中有多少个模块&#xff0c;它们分别是什么&#xff1f;Spring框架的设计目标、设计理念&#xff1f;核心是什么&#xff1f;Spring框架中都用到了哪些设计模式&#xff1f;Spring的核心机制是什么&#xff1f;什么是Spring IOC容器&#xff1f;什么是依…

竞赛选题 python区块链实现 - proof of work工作量证明共识算法

文章目录 0 前言1 区块链基础1.1 比特币内部结构1.2 实现的区块链数据结构1.3 注意点1.4 区块链的核心-工作量证明算法1.4.1 拜占庭将军问题1.4.2 解决办法1.4.3 代码实现 2 快速实现一个区块链2.1 什么是区块链2.2 一个完整的快包含什么2.3 什么是挖矿2.4 工作量证明算法&…

鸿蒙面试心得

自疫情过后&#xff0c;java和web前端都进入了冰河时代。年龄、薪资、学历都成了找工作路上躲不开的门槛。 年龄太大pass 薪资要高了pass 学历大专pass 好多好多pass 找工作的路上明明阳关普照&#xff0c;却有一种凄凄惨惨戚戚说不清道不明的“优雅”意境。 如何破局&am…

修复:cannot execute binary file --- ppc64le 系统架构

前言&#xff1a; 修复node_exporter,引用pprof包&#xff0c;对源码编译后在 Linux 系统下执行程序运行时&#xff0c;发生了报错&#xff0c;报错信息&#xff1a;cannot execute binary file: Exec format error。 开始以为编译有问题&#xff0c;检查发现&#xff1b;该l…

正规的外盘期货开户指南避坑!

一&#xff1a;最正规最靠谱的外盘期货开户方式。那就是直开香港账户&#xff0c;需要基本证件、护照、境外卡等。 如果你满足以上条件&#xff0c;可以直接在香港外盘期货公司的营业部或线上官网开户。 优点&#xff1a;安全正规&#xff0c;银期转账。 缺点&#xff1a;保…

Java - 程序员面试笔记记录 实现 - Part1

社招又来学习 Java 啦&#xff0c;这次选了何昊老师的程序员面试笔记作为主要资料&#xff0c;记录一下一些学习过程。 1.1 Java 程序初始化 Java 程序初始化遵循规则&#xff1a;静态变量优于动态变量&#xff1b;父类优于子类&#xff1b;成员变量的定义顺序&#xff1b; …

1. jenkins持续集成交付

jenkins持续集成交付 一、jenkins介绍二、jenkins的安装部署1、下载jenkins2、安装jenkins3、修改插件下载地址4、初始化jenkins 一、jenkins介绍 持续集成交付&#xff0c; CI/CD 偏开发、项目编译、部署、更新 二、jenkins的安装部署 1、下载jenkins [rootjenkins ~]# wge…

LLM 推理:Nvidia TensorRT-LLM 与 Triton Inference Server

随着LLM越来越热门&#xff0c;LLM的推理服务也得到越来越多的关注与探索。在推理框架方面&#xff0c;tensorrt-llm是非常主流的开源框架&#xff0c;在Nvidia GPU上提供了多种优化&#xff0c;加速大语言模型的推理。但是&#xff0c;tensorrt-llm仅是一个推理框架&#xff0…

算法设计与分析--分布式系统作业及答案

分布式系统 作业参考答案2.1 分析在同步和异步模型下&#xff0c;convergecast 算法的时间复杂性。2.2 G 里一结点从 pr 可达当且仅当它曾设置过自己的 parent 变量。2.3 证明 Alg2.3 构造一棵以 Pr 为根的 DFS 树。2.4 证明 Alg2.3 的时间复杂度为 O(m)。2.5 修改 Alg2.3 获得…

限域传质分离膜兼具高渗透性、高选择性特点 未来应用前景广阔

限域传质分离膜兼具高渗透性、高选择性特点 未来应用前景广阔 分离膜是一种具有选择性透过功能的薄层材料。限域传质分离膜是基于限域传质机制的分离膜&#xff0c;兼具高渗透性、高选择性的特点。限域传质是流体分子通过与其运动自由程相当传质空间的过程&#xff0c;流体分子…

网络安全 DVWA通关指南 Cross Site Request Forgery (CSRF)

DVWA Cross Site Request Forgery (CSRF) 文章目录 DVWA Cross Site Request Forgery (CSRF)DVWA Low 级别 CSRFDVWA Medium 级别 CSRFDVWA High 级别 CSRFDVWA Impossible 级别 CSRF CSRF是跨站请求伪造攻击&#xff0c;由客户端发起&#xff0c;是由于没有在执行关键操作时&a…

推荐一个shp修复工具

我们在《如何解决ArcGIS中数据显示乱码问题》一文中&#xff0c;为你分享过打开shp文件的乱码问题。 现在再为你分享一个shp文件的修复工具&#xff0c;你可以在文末查看该工具的领取方式。 shp文件修复工具 Shapefile&#xff08;简称SHP&#xff09;是Esri推出的一种广泛使…

新能源行业知识体系-------蒙西电网需求侧响应

新能源行业知识体系-------主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/139946830 目录 一、背景介绍二、需求响应电能量收益介绍三、超额回收需求响应减免收益介绍四、参与需求侧响应五、蒙西电力现货特点六、交易中…

好消息!终于解决了!Coze工作流错误中断问题终于得到解决!

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 解决方案 📒📝 常见的工作流中断问题📝 好消息来了!⚓️ 相关链接 ⚓️📖 介绍 📖 大家是否曾经遇到过这样的问题:在Coze平台辛辛苦苦设计的一个工作流,尤其是流程非常复杂和长的情况下,只要中间一个环节出错,整…