『C++成长记』内存管理

🔥博客主页:小王又困了

📚系列专栏:C++

🌟人之为学,不日近则日退

❤️感谢大家点赞👍收藏⭐评论✍️

目录

一、C/C++内存分布

二、内存管理方式

📒2.1C语言内存管理方式 

📒2.2C++内存管理方式 

🎀2.2.1new/delete操作内置类型

🎀2.2.1new和delete操作自定义类型

三、operator new与operator delete函数

四、new和delete的实现原理

📒4.1内置类型

📒4.2自定义类型

五、定位new表达式(placement-new)

📒5.1概念

📒5.2使用格式

📒5.3使用场景

六、malloc/free和new/delete的区别

 七、内存泄漏

📒7.1什么是内存泄漏

📒7.2内存泄漏的危害

📒7.3内存泄漏分类

📒7.4检测内存泄漏

📒7.5避免内存泄漏


一、C/C++内存分布

    在C和C++中,内存分布主要包括以下几个部分:

代码段(Code Segment):

  • 包含程序的机器代码,也称为文本段。
  • 是只读的,存储程序的执行代码。
  • 在程序运行时不可修改

数据段(Data Segment):

  • 包括初始化的全局变量静态变量
  • 在程序开始运行时分配,并在整个程序的执行周期内保持不变
  • 数据区(Data Section): 存储已经初始化的全局变量和静态变量。
  • BSS段(Block Started by Symbol): 存储未初始化的全局变量和静态变量。

堆(Heap):

  • 由程序员分配和释放的内存区域。
  • 动态分配的内存(例如使用newmalloc函数)存储在这里。
  • 堆的大小在程序运行时可以动态调整。

栈(Stack):

  • 存储函数的局部变量和函数调用的上下文信息。
  • 由编译器自动分配和释放。
  • 栈的大小在程序运行时是固定的。

堆栈区域之间的空间(Heap-Stack Gap):

  • 用于防止堆和栈之间的冲突。
  • 可以防止堆溢出覆盖栈数据或栈溢出覆盖堆数据。

 小Tips:这些区域在内存中的布局可能因操作系统和编译器而异,但通常遵循相似的结构。在运行时,栈的地址通常位于较高的内存地址,而堆的地址通常位于较低的内存地址。代码段和数据段的位置取决于可执行文件的加载方式,通常在较高的内存地址。 

二、内存管理方式

📒2.1C语言内存管理方式 

    C语言提供了一些基本的内存管理函数和操作符,使程序能够有效地分配和释放内存。以下是C语言中常用的内存管理方式:

📝malloc() 和 free():

  • malloc()函数用于动态分配指定大小的内存空间。
  • free()函数用于释放先前由malloc()分配的内存空间。
  • 例子:
// 分配10个整数大小的内存空间
int *ptr = (int *)malloc(10 * sizeof(int));
if (ptr != NULL) 
{// 使用内存// ...// 释放内存free(ptr);
}

📝 calloc() 和 realloc():

  • calloc()函数用于分配指定数量和大小的内存空间,并将其初始化为零。
  • realloc()函数用于更改之前分配的内存块的大小。
  • 例子:
// 分配并初始化为0,10个整数大小的内存空间
int *ptr = (int *)calloc(10, sizeof(int));
if (ptr != NULL)
{// 使用内存// ...// 重新分配内存空间为20个整数大小ptr = (int *)realloc(ptr, 20 * sizeof(int));// 使用重新分配后的内存// ...// 释放内存free(ptr);
}

📝静态分配:

  • 静态分配是在编译时为变量分配内存空间。
  • 包括全局变量、静态变量和局部变量(在函数外部定义的变量是静态的)。
  • 内存分配和释放由编译器自动处理。

📝自动变量:

  • 在函数内部定义的变量通常是自动变量。
  • 自动变量的内存分配和释放由程序的执行控制流决定。

📝指针和动态内存分配:

  • 使用指针进行内存分配和释放是C语言中动态内存管理的重要部分。
  • 必须小心管理分配的内存,以避免内存泄漏和悬空指针问题。

 小Tips:C语言中的内存管理是相对低级的,程序员需要手动分配和释放内存。不正确的内存管理可能导致内存泄漏、悬空指针或者堆栈溢出等问题。因此,编写健壮和高效的代码需要仔细管理内存的分配和释放。

📒2.2C++内存管理方式 

    C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理

🎀2.2.1new/delete操作内置类型

void Test()
{// 动态申请一个int类型的空间int* ptr1 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr2 = new int(10);// 动态申请10个int类型的空间,不初始化int* ptr3 = new int[10];// 动态申请10个int类型的空间,并初始化前三个int* ptr4 = new int[10]{1,2,3};delete ptr1;delete ptr2;delete[] ptr3;delete[] ptr4;
}

小Tips:用new开辟空间是不会进行初始化的。如果我们想动态申请并初始化,类型后面要跟圆括号 () ,动态申请多个连续空间,类型后面跟的是方括号 [ ] ,如果要对这多个连续的空间初始化,可以在 [ ] 的后面跟 { }  { } 里面是初始化的数据,初始化的值不够,后面会默认初始化为0。申请和释放单个元素的空间,使用newdelete操作符,申请和释放连续的空间,使用new[ ]delete[ ]

🎀2.2.1new和delete操作自定义类型

class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};int main()
{//通过new动态申请一块空间,存储A类型变量A* p1 = new A(1);delete p1;//通过new动态申请3个连续空间,存储3个A类型的数据A* p2 = new A[3];delete[] p2;return 0;
}

小Tips:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而mallocfree不会。如果自定义类型没有默认构造函数,我们在new的时候就需要人为的传参进行初始化,并且申请了几块空间就要初始化几个,不像动态申请的内置类型数组,可以只初始化前面一部分,后面默认是0。

A* p6 = new A[3]{ 1, 2, 3 };//通过隐式类型转换去初始化
A* p6 = new A[3]{ A(1), A(2), A(3) };//通过匿名对象去初始化

三、operator new与operator delete函数

    newdelete是用户进行动态内存申请和释放的操作符operator newoperator delete系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

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_FINALLYreturn;
}//free的实现#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

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

小Tips:operator newoperator delete不是运算符重载。operator newoperator deletemallocfree在功能上相似,都是到堆上申请空间,然后释放空间,它们的区别在于,前者失败了会抛异常,而后者失败了是返回错误码,通过返回值来表示这一块有问题。

四、new和delete的实现原理

📒4.1内置类型

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

📒4.2自定义类型

📖new的原理

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

📖delete的原理

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

📖new T[N]的原理 

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

📖delete[]的原理

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

五、定位new表达式(placement-new)

📒5.1概念

    定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

📒5.2使用格式

  • new (place_address) type
  • new (place_address) type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表

📒5.3使用场景

    定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如 果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

int main()
{// p1现在指向的是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
有执行A* p1 = (A*)malloc(sizeof(A));new(p1)A;  // 注意:如果A类的构造函数有参数时,此处需要传参p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);return 0;
}

六、malloc/free和new/delete的区别

📖共同点:

都是从堆上申请空间,并且需要用户手动释放。

📖不同点:

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

七、内存泄漏

📒7.1什么是内存泄漏

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

📒7.2内存泄漏的危害

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

📒7.3内存泄漏分类

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

  • 堆内存泄漏(Heap leak)

    堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一 块内存,用完后必须通过调用相应的free或者delete删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak

  • 系统资源泄漏

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

📒7.4检测内存泄漏

    在vs下,可以使用windows操作系统提供的_CrtDumpMemoryLeaks() 函数进行简单检测,该函数只报出了大概泄漏了多少个字节,没有其他更准确的位置信息。

int main()
{int* p = new int[10];// 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏_CrtDumpMemoryLeaks();return 0;
}

因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜 防,简单的可以采用上述方式快速定位下。如果工程比较大,内存泄漏位置比较多,不太好查时一般都是借助第三方内存泄漏检测工具处理的。

📒7.5避免内存泄漏

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

总结:内存泄漏非常常见,解决方案主要分为以下两种:

  1. 事前预防性型,如智能指针等。
  2. 事后查错型,如泄漏检测工具。

🎁结语: 

     本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。你们的支持就是博主最大的动力。

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

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

相关文章

【OpenAI】自定义GPTs应用(GPT助手应用)及外部API接口请求

11月10日&#xff0c;OpenAI正式宣布向所有ChatGPT Plus用户开放GPTs功能 简而言之&#xff1a;GPT应用市场(简称GPTs, 全称GPT Store) Ps&#xff1a; 上图为首次进入时的页面&#xff0c;第一部分是自己创建的GPTs应用&#xff0c;下面是公开可以使用的GPTs应用 一、创建GPTs…

振弦采集仪在桥梁结构健康监测中的应用探索

振弦采集仪在桥梁结构健康监测中的应用探索 振弦采集仪是一种用于测量结构振动特性的仪器&#xff0c;通过采集结构上的振弦信号&#xff0c;可以评估结构的健康状况。在桥梁结构健康监测中&#xff0c;振弦采集仪可以发挥重要的作用。 首先&#xff0c;振弦采集仪能够实时监测…

上传文件:413 Request Entity Too Large Maximum upload size exceeded 解决方案

文章目录 前言一、原因分析二、解决方案1.nginx配置文件2.application.yml配置文件 总结 前言 在上传文件时&#xff0c;如果没有做一些配置的话&#xff0c;会导致上传失败&#xff1a;413 Request Entity Too Large 或者 Maximum upload size exceeded。 提示&#xff1a;以…

geemap学习笔记049:下载Landsat数据时遇到的一个问题

前言 最近在下载Landsat 8 地面反射率数据&#xff08;Surface Reflectance&#xff09;时&#xff0c;遇到了一个问题&#xff0c;无论是使用geemap.ee_export_image_to_drive() 函数还是geemap.download_ee_image() 函数下载的数据&#xff0c;易康都打不开&#xff0c;显示…

【Java】面向对象 OOP

文章目录 面向对象OOP概述一、对象内存相关二、类的成员之一&#xff1a;成员变量&#xff08;Field&#xff09;2.1 如何声明成员变量2.2 成员变量 与 局部变量 三、类的成员之一&#xff1a;成员方法&#xff08;Method&#xff09;3.1 方法调用内存分析3.2 方法的重载3.3 可…

NAS入门(学习笔记)

文章目录 AutoMLNAS初期NAS当前NAS框架One-Shot NAS权重共享策略 Zero-Shot NASZen-NASNASWOTEPENAS 参考资料 AutoML 深度学习使特征学习自动化 AutoML 使深度学习自动化 自动化机器学习 (automated machine learning) 是一种自动化的数据驱动方法, 并做出一系列决策。 按…

第2、3次作业

题目1&#xff1a; 基于域名[www.openlab.com](http://www.openlab.com)可以访问网站内容为 welcome to openlab!!! 题目2&#xff1a; 给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于[www.openlab.com/student](http://www.openlab.c…

RIP【新华三与华为区别】

【介绍】 rip分为rip 1 与 rip 2 &#xff0c;rip 2 是对 rip 1 的一种升级&#xff0c;rip 2 可以进行认证等功能 【命令】 新华三&#xff1a; [HC3-R1] rip #启用rip [HC3-R1-rip] version 2 #告知rip 版本号 [HC3-R1-rip] network 192.168.1.0 #宣告其网段 [HC3-R1-rip] …

【python】py-spy 实时显示python进程内的线程堆栈CPU消耗 python CPU消耗分析

安装 pip install py-spy AI调用源码&#xff0c;红色调用时&#xff0c;python进程CPU 100% 启动程序&#xff0c;输入问题&#xff0c;观察CPU top sudo .local/bin/py-spy top --pid 7150 可以看到&#xff0c;此时与显卡交互占用了绝大部分CPU&#xff0c;有点死循环检测…

AI大模型预先学习笔记一:transformer和fine tune技术介绍

一、商业观点&#xff1a;企业借助大模型获得业务增长可能 二、底层原理&#xff1a;transformer 1&#xff09;备注 ①下面每个步骤都是自回归的过程&#xff08;aotu-regressive&#xff09;&#xff1a;已输出内容的每个字作为输入&#xff0c;一起生成下一个字 ②合起来就…

全自动网页制作系统流星全自动网页生成系统重构版输入网页信息即可制作

源码优点: 所有模板经过精心审核与修改&#xff0c;完美兼容小屏手机大屏手机&#xff0c;以及各种平板端、电脑端和360浏览器、谷歌浏览器、火狐浏览器等等各大浏览器显示。 免费制作 为用户使用方便考虑&#xff0c;全自动网页制作系统无需繁琐的注册与登入&#xff0c;直接…

CUTANA™ CUTRUN Assays ——实现超敏基因组定位

蛋白质和核酸是构成生命体最为重要的两类生物大分子&#xff0c;二者间的相互作用一直是分子生物学研究的中心问题之一。研究细胞内蛋白质-DNA相互作用的常用方法是染色质免疫沉淀(Chromatin Immunoprecipitation, ChIP) &#xff0c;同时ChIP还常被用于确定基因组上与组蛋白修…

MybatisPlus框架--如何让公共字段自动更新插入

目录 建一个springboot项目 引入mybatisplus依赖 功能区 如何让公共字段自动更新插入&#xff08;createTime&#xff0c;updateTime&#xff09; 1.创建表 2.项目 3.Application 4.application.yml 5.pom.xml 6.entity 7.mapper 8.service 9.service下的impl 10…

使用scipy处理图片——滤镜处理

大纲 black_tophatwhite_tophatconvolvecorrelategaussian_filtergaussian_laplacemaximum_filtermedian_filterminimum_filterpercentile_filterprewittrank_filtersobelspline_filteruniform_filter基础代码代码仓库 在《使用numpy处理图片——模糊处理》一文中&#xff0c;我…

【绩效考核】分享:海底捞的考核方法

很多企业都希望学习海底捞的管理方法。在绩效考核方面&#xff0c;海底捞的“不考核利润、考核满意度”始终是一大特色。它重视门店管理者的绩效考核&#xff0c;并将客户满意度作为绩效考核的核心&#xff0c;这对其他企业是否具有借鉴意义&#xff1f;海底捞始终秉承“服务至…

MySQL——性能优化与关系型数据库

文章目录 什么是性能&#xff1f;什么是关系型数据库&#xff1f;数据库设计范式 常见的数据库SQL语言结构化查询语言的六个部分版本 MySQL数据库故事历史版本5.6/5.7差异5.7/8.0差异 什么是性能&#xff1f; 吞吐与延迟&#xff1a;有些结论是反直觉的&#xff0c;指导我们关…

ioctl操作实现

ioctl&#xff0c;避免使用三个全局变量&#xff0c;因此写进一个结构体里面 ioctl对文件属性进行操作 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <asm/uaccess.h>#define…

微信商家转账到零钱怎么开通?场景模板

商家转账到零钱是什么&#xff1f; 使用商家转账到零钱这个功能&#xff0c;可以让商户同时向多个用户的零钱转账。商户可以使用这个功能用于费用报销、员工福利发放、合作伙伴货款或分销返佣等场景&#xff0c;提高效率。 商家转账到零钱的使用场景有哪些&#xff1f; 商家…

pytest -- 进阶使用详解

pytest-html⽣成报告 Pytest-HTML 是⼀个插件&#xff0c;它可以⽣成漂亮且易于阅读的 HTML 测试报告。 pytest-html ⽣成报告的步骤 ① 安装 pytest-html 插件&#xff1a; pip install pytest-html ② 运⾏测试并⽣成报告&#xff1a; file name:main.pyimport pytest&qu…

Spring IoC 和 DI

文章目录 1. 什么是 Spring2. 什么是 IoC3. 什么是 DI4. IoC & DI 使用5. 获取 Bean 的方式5.1 根据类型获取 bean5.2 根据名称获取 bean5.3 获取bean对象的其他方式5.4 五大注解的关联 6. 方法注解7. 扫描路径8. Bean 的名称9. DI 详解9.1 属性注入9.2 构造方法注入9.3 Se…