【C++】内存管理

2023-10-31

目录

一、C/C++内存分布

二、C语言中动态内存管理方式

三、C++中动态内存管理

1、开辟空间

2、释放空间

四、operator new与operator delete函数

五、内存泄漏

1、什么是内存泄漏

2、如何避免内存泄漏

总结


一、C/C++内存分布

对于我们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";
 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);
}

   选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)
   globalVar在哪里?____   staticGlobalVar在哪里?____
   staticVar在哪里?____   localVar在哪里?____
   num1 在哪里?____
   
   char2在哪里?____   *char2在哪里?___
   pChar3在哪里?____      *pChar3在哪里?____
   ptr1在哪里?____        *ptr1在哪里?____

答案:C    C    C    A    A    

           A    A    A    D    A    B

char2是一个数组,将代码段的abcd拷贝到栈

pchar3是一个指针,它是指向代码段的abcd


以及,我们经常说的内存泄漏,通常指的是堆区的空间,因为只有堆区我们才能操控,其它区域的空间都是我们操纵不了的。

二、C语言中动态内存管理方式

C语言动态内存管理通常使用malloc/calloc/realloc/free 等函数,操作比较麻烦,需要我们手动1计算开辟空间大小,以及强制类型转换

例:

void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
free(p3 );
}

我们1并不需要free(p2),因为我们的p3创建是通过realloc,在p2指向的空间的基础上再次扩容。

如果空间不够了,会自动free掉p2指向的空间,在另一个地方重新开辟新的空间。

三、C++中动态内存管理

C++中使用的是new和delete。

它们两个是操作符,并不是函数

1、开辟空间

例:

int* p0 = new int;//申请一个int类型的对象


int* p1 = new int(0);//申请一个int类型的对象并初始化为0


int* p2 = new int[5]{ 0 };//申请一个int类型的数组,数组中有5个元素,并且将数组初始化为0

C++98是不支持对new出来的数组进行初始化的

C++11支持,数组初始化有以下几种

int* p2 = new int[5]{ 0 };

int* p3 = new int[5]{ 0,1,2,3,4 };

int* p4 = new int[5]{ 0,1,2 };

与普通的数组初始化方式是类似的

2、释放空间

释放空间是利用delete操作符来进行的

释放普通的变量是直接delete + 变量名

释放聚合类型,例如数组要加上[]  delete[ ] + 变量名

例:

void test1()
{
	int* p0 = new int;
	int* p1 = new int(0);
	int* p2 = new int[5]{ 0 };
	int* p3 = new int[5]{ 0,1,2,3,4 };
	int* p4 = new int[5]{ 0,1,2 };

	delete p0;
	delete p1;
	delete[] p2;
	delete[] p3;
	delete[] p4;
}

new和delete花括号要对应,如果不对应,可能会出现一定的问题。

四、operator newoperator delete函数

对于内置类型来说,new/delete 和 malloc/free 没有本质区别,只有用法区别。

对于自定义类型:new会调用构造函数,如果不传参数会调用默认构造函数

new可以传参,调用全缺省构造函数或者是非默认构造函数

class A
{
public:
	A(int a,int b, int c)
		:_a(a)
		,_b(b)
		,_c(c)
	{
		cout << "A()" << this << endl;
	}
	~A()
	{
		cout << "~A" << this << endl;
	}

	void show()
	{
		cout << _a << "  " << _b << "  " << _c << endl;
	}
private:
	int _a;
	int _b;
	int _c;
};

void test2()
{
	A* pa = new A(1, 2, 3);
	pa->show();

	delete pa;
}

delete调用析构函数,清理对象中的资源,释放空间,如果不delete对象,该对象就不能调用析构函数

这是delete之后的情况,我们在构造函数和析构函数中打印了一句话,证明调用了它们两个

然后我们去掉delete

 

发现并没有调用析构函数。


我们一定想要知道如何对new出来的多个对象初始化。


void test3()
{
	A* pa1 = new A[2]{ A(2,3,4) ,A(4,3,5) };

	A* pa2 = new A[2]{ {1,2,3},{2,3,4} };

	delete[] pa1;
	delete[] pa2;
}

 以上就是两种,初始化的方式。


接下来回归正题,operator new 和operator delete是new和delete的底层。

我们知道malloc失败会返回NULL,new失败会出现什么情况呢?

new失败会抛异常,并不需要检查返回值

void test4()
{
	try
	{
		char* p2 = new char[1024u * 1024u * 1024u * 2 - 1];
		printf("%p\n", p2);
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
}


我们还以上面的代码为例,调出反汇编,我们重点观察标红的位置,我们发现new一个对象时,会调用operator new和A的构造函数。

下面是operator new和operator delete的底层实现

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试
执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
    
 // try to allocate size bytes
 void *p;
 while ((p = malloc(size)) == 0)
 if (_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_FINALLY
	
	 return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

 我们观察operator new和operator delete的底层是调用了malloc和free,并且加入了一些对于异常的判断。将malloc和free进行了封装。

operator new和operator delete 对于我们与malloc和free没有什么区别,我们也没有什么必须使用它的理由。

operator new和operator delete的用法与malloc和free没有区别


对于operator new和operator delete的重载,我们可以重载,在实现原本功能的基础上,加入判断内存泄漏的机制

我们也可以写一个类的专属operator new和operator delete,例如创建一个内存池。

严格意义上来说这并不是函数重载,因为专属的operator new是在类域中,而全局operator new

在全局作用域中,二者并不构成重载。

五、内存泄漏

1、什么是内存泄漏

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。

随着计算机应用需求的日益增加,应用程序的设计与开发也相应的日趋复杂,开发人员在程序实现的过程中处理的变量也大量增加,如何有效进行内存分配和释放,防止内存泄漏的问题变得越来越突出。例如服务器应用软件,需要长时间的运行,不断的处理由客户端发来的请求,如果没有有效的内存管理,每处理一次请求信息就有一定的内存泄漏。这样不仅影响到服务器的性能,还可能造成整个系统的崩溃。因此,内存管理成为软件设计开发人员在设计中考虑的主要方面 。

2、如何避免内存泄漏

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps:
这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智
能指针来管理才有保证。
2. 采用RAII思想或者智能指针来管理资源。
3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
4. 出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵。

总结


例如:以上就是今天要讲的内容,本文仅仅简单介绍了C++的动态内存管理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】内存管理 的相关文章

  • 编译时运算符

    有人可以列出 C 中可用的所有编译时运算符吗 C 中有两个运算符 无论操作数如何 它们的结果始终可以在编译时确定 它们是sizeof 1 and 2 当然 其他运算符的许多特殊用途可以在编译时解决 例如标准中列出的那些整数常量表达式 1 与
  • EF Core Group By 翻译支持条件总和

    听说 EF Core 2 1 将支持翻译小组 我感到非常兴奋 我下载了预览版并开始测试它 但发现我在很多地方仍然没有得到翻译分组 在下面的代码片段中 对 TotalFlagCases 的查询将阻止翻译分组工作 无论如何 我可以重写这个以便我
  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • 按成员序列化

    我已经实现了template
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • 查找c中结构元素的偏移量

    struct a struct b int i float j x struct c int k float l y z 谁能解释一下如何找到偏移量int k这样我们就可以找到地址int i Use offsetof 找到从开始处的偏移量z
  • 嵌套接口:将 IDictionary> 转换为 IDictionary>?

    我认为投射一个相当简单IDictionary
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • WCF 中 SOAP 消息的数字签名

    我在 4 0 中有一个 WCF 服务 我需要向 SOAP 响应添加数字签名 我不太确定实际上应该如何完成 我相信响应应该类似于下面的链接中显示的内容 https spaces internet2 edu display ISWG Signe
  • 显示UnityWebRequest的进度

    我正在尝试使用下载 assetbundle统一网络请求 https docs unity3d com ScriptReference Networking UnityWebRequest GetAssetBundle html并显示进度 根
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • 向现有 TCP 和 UDP 代码添加 SSL 支持?

    这是我的问题 现在我有一个 Linux 服务器应用程序 使用 C gcc 编写 它与 Windows C 客户端应用程序 Visual Studio 9 Qt 4 5 进行通信 是什么very在不完全破坏现有协议的情况下向双方添加 SSL
  • 通过指向其基址的指针删除 POD 对象是否安全?

    事实上 我正在考虑那些微不足道的可破坏物体 而不仅仅是POD http en wikipedia org wiki Plain old data structure 我不确定 POD 是否可以有基类 当我读到这个解释时is triviall
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • 如何防止用户控件表单在 C# 中处理键盘输入(箭头键)

    我的用户控件包含其他可以选择的控件 我想实现使用箭头键导航子控件的方法 问题是家长控制拦截箭头键并使用它来滚动其视图什么是我想避免的事情 我想自己解决控制内容的导航问题 我如何控制由箭头键引起的标准行为 提前致谢 MTH 这通常是通过重写

随机推荐

  • react的状态提升

    1 概念理解 在react中是单向数据流的设计 即 只有父组件可以传递数据给子组件 而没有子组件传递数据给父组件的概念 以正确的技术说明 是 拥有者组件 可以设置 被拥有者组件 中的资料 也就是主人与仆人的关系 那么子组件要传递数据给父组件
  • 宝塔 + SSRPanel 前端安装教程

    一 安装宝塔及环境 安装宝塔面板 登录 SSH 后 直接安装宝塔 yum install y wget wget O install sh http download bt cn install install sh sh install
  • 数据库定义语言(DDL)详解

    数据库定义预压 DDL 语言 是重要的语言之一 包含三个关键字 CREATE DROP ALTER 数据库语言都遵循巴科斯范式 其语言规范如下所示 L利用CREATE关键字可以实现定义 定义数据库 定义表 定义索引 定义视图等 常用的是建表
  • LOESS局部加权

    参考资料 1 Matlab中fLOESS函数 https ww2 mathworks cn matlabcentral fileexchange 55407 loess regression smoothing s tid srchtitl
  • node.js 做反向代理

    1 安装http proxy模块 npm install http proxy 2 使用http proxy模块启动代理服务器 导入http模块 var http require http 导入http proxy模块 var httpPr
  • C++设计模式(二)(摘录)

    C 设计模式 二 摘录 装饰模式 装饰模式 动态地给一个对象添加一些额外的功能 它是通过创建一个包装对象 也就是装饰来包裹真实的对象 新增加功能来说 装饰器模式比生产子类更加灵活 以下情形考虑使用装饰模式 需要扩展一个类的功能 或给一个类添
  • java之Thread类详细分析(全)

    目录 前言 1 属性值 1 1 线程属性方法 2 常用方法 3 使用方法 前言 Thread是程序中的执行线程 jvm并发地运行多个执行线程 1 属性值 通过查看Thread源码 是继承Runnable接口的实现类 无论使用Runnable
  • [Java]学习笔记

    一些JAVA的学习笔记 记录 主要是和C的不同点 https www runoob com java java tutorial html 声明数组变量 注意 建议使用 dataType arrayRefVar 的声明风格声明数组变量 da
  • 【多同步挤压变换】基于多同步挤压变换处理时变信号和噪声信号研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 2 1 算例1 2 2 算例2 2 3 算例3 2 4 算例4 2 5 算例5 3 参考文献
  • c++版本itk读取dicom序列

    读取dicom序列 hablee void readDicomSeries const std string dicom dir itk Image
  • .NET MVC+EF CodeFirst+IOC+EasyUI 框架设计教程(概述)

    一 开发环境 Win7 VS2013 C SQL Server2008 R2 二 总体源代码结构如下 说明 这个分层按个人理解 不一定要照我的 如果做过开发 会三层架构之类的理解起来就容易 01 Infrastructure 基础构件 Ne
  • QGIS批量将分幅遥感影像合并

    要在QGIS中编程实现批量将分幅遥感影像拼接成完整影像 可以按照以下步骤进行操作 1 首先 确保已经安装并正确配置了QGIS Python环境 并导入所需的库 以下是代码的导入部分 from qgis core import QgsProj
  • 常见的金融术语——176个基础金融知识名词解释

    基金类 1 开放式基金 open end funds 指基金规模不是固定不变的 而是可以随时根据市场供求情况发行新份额或被投资人赎回的投资基金 2 封闭式基金 close end funds 指基金规模在发行前已确定 在发行完毕后及规定的期
  • 解决文字自动换行,字母、数字不换行问题

    添加css属性word break break all
  • mysql修改root密码的语句

    ALTER USER root localhost IDENTIFIED WITH mysql native password BY 新密码 其中 localhost 字符 有的小伙伴可能遇到过无法通过navicat连接Linux中的MyS
  • 单点登录SSO:可一键运行的完整代码

    单点登录方案不同于一个普通站点 它的部署比较繁琐 涉及到好几个站点 要改host 安装证书 配置HTTPS 看到的不少这方面示例都是基于HTTP的 不认同这种简化 1 它体现不出混合HTTP HTTPS时 单点注销要注意处理的问题 2 做单
  • 【Node.js】下载安装及简单使用

    说起Node js 它是当前市面上非常受欢迎的框架 允许我们使用JavaScript搭建后端应用 它有着种种优点 诸如 非阻塞I O 事件驱动 跨平台 高性能 单线程 等等等等 不过现在我们不必执拗与关心这些优点的含义 当务之急是先上手他
  • conda加速设置

    Conda作为使用最为便捷的python环境管理工具 可以协助我们很方便的下载安装第三方库 软件包等操作 但其在下载资源的过程中速度不言而喻 尤其是在更换国内源的情况下 下载速度没有实质性的改变是很令人头疼的一件事 Mamba 树眼镜蛇 能
  • (tensorflow学习)用Object Detection API实现摄像头实时物体检测

    对于物体识别 谷歌已经有训练好的模型供我们使用 图方便不想自己训练的可以直接使用 说实话 装这个tensorflow真心麻烦 我建议用anaconda环境搭建 还要注意装的话装1 几的版本就可 用gpu跑的话注意显卡型号和版本是否兼容 真是
  • 【C++】内存管理

    目录 一 C C 内存分布 二 C语言中动态内存管理方式 三 C 中动态内存管理 1 开辟空间 2 释放空间 四 operator new与operator delete函数 五 内存泄漏 1 什么是内存泄漏 2 如何避免内存泄漏 总结 一