C++好难(5):内存管理

news2024/9/23 17:17:48

这一节学完,我们 C嘎嘎 就算是正式入门了,但是之后的课还会更上一阶d(ŐдŐ๑)  继续坚持!


【本节目标】

1. C/C++内存分布

2. C语言中动态内存管理方式

3. C++中动态内存管理

4. operator new与operator delete函数

5. new和delete的实现原理

6.常见问题


目录

【本节目标】

1. C/C++的内存分布

2.C语言中的动态内存管理方式:malloc/calloc/realloc/free

1)malloc

2)calloc

3)realloc

4)free

3.C++内存管理方式

3.1new和delete的基本操作

3.2new和delete操作符自定义类型

3.3总结:

4.operator new与operator delete函数

5.new和delete的实现原理

5.1内置类型

5.2自定义类型

6.常见问题:

1)malloc/free和new/delete的区别

2)内存泄漏

3)内存泄漏分类

4)如何避免内存泄漏


我们都知道在 C语言 中,可以使用 mallocrealloccalloc 来开辟空间,使用 free 来销毁空间

那我们 C++ 应该怎样去申请空间呢?

1. C/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";
	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.选择题

globalVar 存储于 数据段

 
staticGlobalVar 存储于 数据段

 
staticVar 存储于 数据段

 
localVar 存储于

 
localVar1 存储于

 
num1 存储于


char2 存储于

因为 char2 是一个数组,存在栈上面的,而 “abcd” 是存在常量区的常量,只是拷贝给了 char2
 
*char2 存储于

数组名就是首元素的地址,也就是 char 是地址,*char2 相当于对 cahr2 进行解引用,找到它的内容,那么它的内容是存储在栈上面的
 
pChar3 存储于

pChar3 是一个指针变量,这个指针变量是在栈上面开的
 
*pChar3 存储于 代码段

pChar3 是一个指针变量,它存的是一个地址,它指向常量区的字符串 “a b c d”,*pChar 就是对这个指针变量解引用,找到了它的内容,也就是 “abcd” ,所以它是存在代码段的
 
ptr1 存储于
 
*ptr1 存储于

2.填空题

注意:sizeof 是求字节大小,strlen 是求字符串长度的。
 
sizeof(num1) = 40(算对象占用空间的大小)
 
sizeof(char2) = 5(char2 是一个字符数组,求大小要计算 ‘\0’)
 
sizeof(pChar3) = 4/8 (指针在 32 位平台大小是 4,64 位平台大小是 8)
 
sizeof(ptr1) = 4/8
 
strlen(char2) = 4(char2 是一个字符数组,求长度不计算 \0)
 
strlen(pChar3) = 4

其内存分布图如下:

 

说明:

  • 1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  • 2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
  • 3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
  • 4. 数据段--存储全局数据和静态数据。
  • 5. 代码段--可执行的代码/只读常量。

2.C语言中的动态内存管理方式:malloc/calloc/realloc/free

1)malloc

malloc 函数的功能是开辟指定字节大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个 NULL(空指针)。

 

它使用的时候,传参只需传入需要开辟的字节个数。

2)calloc

calloc 函数的功能也是开辟指定大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个 NULL(空指针)。

calloc 函数传参时需要传入开辟的内存用于存放的元素个数每个元素的大小

calloc 函数开辟好内存后会将空间内容中的每一个字节都初始化为 0。

3)realloc

realloc 函数可以调整已经开辟好的动态内存的大小
第一个参数是需要调整大小的动态内存的首地址,第二个参数是动态内存调整后的新大小。

如果第一次使用realloc时检测到首地址未开空间,则realloc和malloc的功能一致

注意:

  • 原地扩。需扩展的空间后方有足够的空间可供扩展,此时,realloc 函数直接在原空间后方进行扩展,并返回该内存空间首地址(即原来的首地址)。
  • 异地扩。需扩展的空间后方没有足够的空间可供扩展,此时,realloc 函数会在堆区中重新找一块满足要求的内存空间,把原空间内的数据拷贝到新空间中,并主动将原空间内存释放(即还给操作系统),返回新内存空间的首地址。
  • 扩容失败。需扩展的空间后方没有足够的空间可供扩展,并且堆区中也没有符合需要开辟的内存大小的空间。结果就是开辟内存失败,返回一个 NULL(空指针)。

4)free

free 函数的作用就是将 malloc、calloc 以及 realloc 函数申请的动态内存空间释放,其释放空间的大小取决于之前申请的内存空间的大小。

3.C++内存管理方式

C 语言内存管理方式在 C++ 中可以继续使用但C++有更简单的用法:

通过 new delete 操作符进行动态内存管理。

3.1new和delete的基本操作

(1)new  一个 int 类型的对象

int main()
{
	// 用malloc动态申请一个int类型的空间
	int* p1 = (int*)malloc(sizeof(int));
	// 销毁p1
	free(p1);


	// 动态申请一个int类型的空间
	int* p1 = new int;
	// 销毁p1
	delete p1;

	return 0;
}

(2)new 10 个 int 类型的对象

int main()
{
	// 用malloc动态申请一个int类型的空间
	int* p2 = (int*)malloc(10 * sizeof(int));
	// 销毁
	free(p2);


	// 动态申请一个int类型的空间
	int* p2 = new int[10];
	// 销毁
	delete[] p2;

	return 0;
}

delete[ ] 对应的是new[ ] ,是申请多个空间时的样子。 

(3)new 一个 int 类型对象,然后初始化为 10

{
	int* p3 = (int*)malloc(sizeof(int));
	*p3 = 10; //赋值
	//销毁
	free(p3);


	// 动态申请一个int类型的空间并初始化为10
	int* p3 = new int(10);
	// 销毁
	delete p3;

	return 0;
}

(4)new 10 个 int 类型对象,并进行初始化

int main()
{
	//动态申请10个int类型的空间并初始化为1到10
	int* p8 = (int*)malloc(sizeof(int) * 10); //申请
	for (int i = 0; i < 10; i++) //赋值
	{
		p8[i] = i;
	}
	free(p8); //销毁

	//动态申请10个int类型的空间并初始化为1到10
	int* p4 = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	//销毁p4
	delete[] p4;

	return 0;
}

当然如用new在赋值的时候,如果只给前面的控件赋值,后面的空间会赋值为0

 总结如下图:

  • 申请和释放 单个 元素的空间,使用 new 和 delete 
  • 申请和释放 连续 的空间,使用 new[ ] 和 delete[ ]

3.2new和delete操作符自定义类型

对于内置类型来说,malloc new 用法几乎一样,
但是对于自定义类型来说,newdelete相比于malloc,会调用构造函数和析构函数

这里以链表为列,看看malloc和new的区别

malloc:

//链表
struct ListNode
{
    ListNode* next;
    int val;
};

//申请节点
struct ListNode* BuyListNode(int x) 
{
    struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
    assert(node);

    node->next = NULL;
    node->val = x;
    return node;
}

int main()
{
    // 定义n1节点
    struct ListNode* n1 = BuyListNode(1);
    free(n1);
    return 0;
}

new:

//链表
class ListNode
{
public:
    //构造函数
    ListNode(int val = 0)
        :_next(nullptr), _val(val) // 初始化列表
    {
        cout << "ListNode" << endl;
    }
    ~ListNode()
    {
        cout << "~ListNode" << endl;

    }
private:
    ListNode* _next;
    int _val;
};

int main()
{
    // 定义n1节点
    ListNode* n2 = new ListNode(2); // new会去调用ListNode的构造函数
    delete n2;

    return 0;
}

 

3.3总结:

  • 1)malloc/free是函数,而new/delete是关键字
  • 2)C++ 中如果是申请内置类型的对象或是数组,用 new/delete 和 malloc/free 没有什么区别。
  • 3)如果是自定义类型的话,new 和 delete 分别是 开空间+构造函数、析构函数+释放空间,而 malloc 和 free 仅仅是 开空间和释放空间,可以看到区别还是很大的。
  • 4)建议在 C++ 中无论是内置类型还是自定义类型的申请和释放,尽量都使用 new 和 delete。

4.operator new与operator delete函数

new和delete是用户进行动态内存申请和释放的操作符operator new 和operator delete是系统提供的全局函数

new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

 operator new 实际也是通过 malloc 来申请空间,如果 malloc 申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。
operator delete 最终是通过 free 来释放空间的。

5.new和delete的实现原理

5.1内置类型

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

5.2自定义类型

1)new的原理

  • 1. 调用operator new函数申请空间
  • 2. 在申请的空间上执行构造函数,完成对象的构造

2)delete的原理

  • 1. 在空间上执行析构函数,完成对象中资源的清理工作
  • 2. 调用operator delete函数释放对象的空间

3)new T[N] 的原理

  • 1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  • 2. 在申请的空间上执行N次构造函数

4)delete[ ] 的原理

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

6.常见问题:

1)malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。

不同的地方是:

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

2)内存泄漏

什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

 

内存泄漏的危害:

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

3)内存泄漏分类

C/C++程序中一般我们关心两种方面的内存泄漏:

  • 堆内存泄漏(Heap leak)

堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。

假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生堆内存泄漏(Heap leak)

  • 系统资源泄漏

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

4)如何避免内存泄漏

(1)工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。

        ps:这个是理想状态,但是如果碰上异常时,就算注意释放了,还是可能会出问题。可能
        要智能指针来管理才有保证。

(2)采用 RAII 思想或者智能指针来管理资源。

(3)有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

(4)出问题了使用内存泄漏工具检测。(ps:不过很多工具都不够靠谱,或者收费昂贵)。

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

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

相关文章

【在线OJ项目】项目环境与项目演示

目录 一、项目环境 二、项目展示 项目Gitee地址&#xff1a;online-oj: 在线OJ项目实现了核心模块&#xff0c;后续添加竞赛、考试等 (gitee.com)https://gitee.com/PG1886/online-oj 一、项目环境 采用SpringBootSpringMVCMybatis进行服务器开发 前端采用HTMLCSSJS&#…

mysql 5.7.32安装及主从安装信息

最方便的 就是 直接使用docker容器 搭建一个比较方便 或者 直接使用yum源安装&#xff0c;说白了就是少踩坑。 或者 是直接使用 宝塔等工具帮忙&#xff0c;直接脚本跑 宝塔面板 - 简单好用的Linux/Windows服务器运维管理面板 以下是内网两台机器安装的方法 1&#xff1a; 下…

【数据结构】ArrayList与顺序表

目录 1.List接口 2.线性表 3.顺序表 3.1常用方法 3.2常用方法的实现 4.ArrayList 4.1构造方法 4.2遍历 4.3扩容 4.4CVTE面试题&#xff1a;删除相同字符 5.ArrayList的具体实现 5.1洗牌算法 5.2杨辉三角 6.ArrayList的优缺点 1.List接口 List 接口继承于 Collec…

3 天,入门 TAURI 并开发一个跨平台 ChatGPT 客户端

TAURI 是什么 TAURI 是一个使用 Rust 编写的程序框架&#xff0c;它允许我们使用 Web 技术和 Rust 语言构建跨端应用。它提供了大量特性&#xff0c;例如系统通知、网络请求、全局快捷键、本地文件处理等&#xff0c;它们都可以在前端通过 JavaScript 便捷的调用。 TAURI 应用…

《精英的傲慢:好的社会该如何定义成功》笔记与摘录

目录 作者简介 书内容简介 经典摘录 1、现状与现象 2、什么是优绩至上原则 3、对优绩至上原则赞同与否的讨论 4、 优绩至上原则存在的争议点 5、 作为哲学家&#xff0c;桑德尔从道德哲学角度的思考 6、作者对优绩制的批判 7、流动性与平等的关系 8、我们该如何摆脱优…

MyCat分片-垂直分库

文章目录 需求场景一、环境准备二、实现1.MyCat—schema.xml文件配置2.MyCat—server.xml文件配置3.MyCat启动4.MyCat登录5.创建表结构及数据导入 三、全局表配置全局表配置 此文档来源于网络,如有侵权&#xff0c;请联系删除&#xff01; 需求场景 在业务系统中&#xff0c;涉…

使用ChatGPT辅助学习——让你的学生主动找到学习的方法!

ChatGPT就像一座巨大的金矿&#xff0c;能挖到多少金子&#xff0c;完全取决于你的思维、认知和行动力。 当大部分人还在观望&#xff0c;或者拿着ChatGPT随便玩一玩的时候。 有的人&#xff0c;已经快速把它切入垂直领域&#xff0c;开始深耕。 如果你的孩子或者学生正在上初…

静态库和动态库的制作与使用

1.静态库的制作与使用 小知识&#xff1a;删除命令行&#xff0c;或者是配置好的路径之类的&#xff1a;退出编辑模式后&#xff1a;dd 保存并退出&#xff1a;退出编辑模式后&#xff0c;&#xff1a;wq (1)静态库的制作 1.首先生成你需要加入的文件的.O文件。使用如下代码 …

网络编程六--UDP服务器客户端

写在前面 UDP&#xff08;User Datagram Protocol&#xff09;称为用户数据报协议&#xff0c;是一种无连接的传输协议。 UDP的主要应用在即使丢失部分数据&#xff0c;也不影响整体效果的场景。例实时传输视频或音频时&#xff0c;即使丢失部分数据&#xff0c;也不会影响整…

C++11大杂烩

C11大杂烩 文章目录 C11大杂烩介绍语法统一的列表初始化&#xff1a;{}初始化initializer_list简化声明的方式autotypeid().name():获取类型名decltype nullptr范围for循环stl库中的一些变化arrayforward_list final和override右值引用和移动语义左值引用和右值引用 移动构造和…

有没有好用的UI在线设计工具?这5个设计师必备!

这篇文章介绍了 5 款在线UI设计工具&#xff0c;分别是即时设计、InVision Studio、Axure、Framer 和 Principle。其中&#xff0c;即时设计是一款次世代的在线协作UI设计工具&#xff0c;支持多人协同在线设计一键交付、插入交互式动画等功能&#xff0c;最近还更新了全球首款…

网络基础知识1—网络

文章目录 1.网络划分1.1局域网&#xff08;内网&#xff09;1.2广域网&#xff08;公网&#xff09; 2.网络的作用3.端口号3.1作用3.2两台主机中的进程传输数据3.3格式3.4注意 4.协议4.1概念4.2三要素4.3最终体现4.4作用 5.五元组5.1源IP5.2源端口5.3目的IP5.4目的端口5.5协议 …

MySQL中这14个神仙功能

1.group_concat 在我们平常的工作中&#xff0c;使用group by进行分组的场景&#xff0c;是非常多的。 比如想统计出用户表中&#xff0c;名称不同的用户的具体名称有哪些&#xff1f; 具体sql如下&#xff1a; select name from user group by name;但如果想把name相同的c…

IO 流学习总结

一&#xff1a;IO 流的概述 1. 什么是 IO 流&#xff1f; 存储和读取数据的解决方法 I&#xff1a;input O&#xff1a;output 流&#xff1a;像水流一样传输数据 2. IO 流的作用&#xff1f; 用于读写数据&#xff08;本地文件&#xff0c;网络&#xff09; 3. IO 流按…

三年亏百亿仍要造“跑车”,哪吒还有几次试错?

文丨智能相对论 作者丨leo陈 燃油车时代&#xff0c;国产品牌没有一款真正意义上成功的“低价跑车”&#xff0c;那在新能源时代&#xff0c;“电”是否可以创造这种可能&#xff1f; 第一个交出答卷的是哪吒汽车。不久前&#xff0c;哪吒发布首款纯电跑车“哪吒GT”&#x…

3个方法提高电脑运行速度,亲测有效!

案例&#xff1a;怎样提高电脑运行的速度&#xff1f; 【随着使用时间的增长&#xff0c;我的电脑运行速度越来越慢&#xff0c;这样我感到十分不方便和烦恼。有什么办法可以提高电脑的运行速度吗&#xff1f;】 在日常使用电脑过程中&#xff0c;我们难免会遇到电脑运行缓慢…

【C++】第13章: 类继承

文章目录 第十三章 类继承13.1 一个简单的基类13.1.1 派生一个类13.1.2 构造函数&#xff1a;访问权限的考虑13.1.3 使用派生类13.1.4 派生类和基类之间的特殊关系 13.2 继承&#xff1a;is-a关系13.3 多态公有继承13.4 静态联编与动态联编13.4.1 指针和引用类型的兼容性13.4.2…

Doris简介、部署、功能介绍以及架构设计

Doris简介、部署、功能介绍以及架构设计 1. Doris简介 Doris 中文官方文档&#xff1a;https://doris.apache.org/zh-CN/docs/dev/summary/basic-summary 1.1 Doris概述 ​ Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人…

5.10晚间黄金CPI精准分析及多空交易策略

近期有哪些消息面影响黄金走势&#xff1f;本周黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周三&#xff08;5月10日&#xff09;亚欧盘中&#xff0c;现货黄金震荡下跌&#xff0c;现报2030美元/盎司&#xff0c;稍早一度触及2038美元/盎司高点。美联储理事…

day30_jdbc

今日内容 零、 复习昨日 一、作业 二、SQL注入 三、PreparedStatement 四、事务 五、DBUtil 零、 复习昨日 见晨考 一、作业 package com.qf.homework;import com.qf.entity.User;import java.sql.*; import java.text.SimpleDateFormat; import java.util.ArrayList; import …