

**前引:**C++的内存管理是编程中的一把「双刃剑」------它赋予开发者极致的性能控制权,却也暗藏内存泄漏、悬垂指针、碎片化等致命陷阱。无论是初学者的 new/delete 误用,还是资深工程师的智能指针循环引用问题,内存相关的缺陷往往成为代码质量的重中之重!本文从底层原理出发,直击内存分配的运行机制(如堆栈交互、数据分配策略),结合现代C++特性,剖析高并发场景下的性能优化策略~正文开始!
目录
[operator new与operator delete函数](#operator new与operator delete函数)
[operator new的底层实现](#operator new的底层实现)
[operator delete的底层实现](#operator delete的底层实现)
[new[ ]的原理](#new[ ]的原理)
[delete[ ]的原理](#delete[ ]的原理)
C\C++内存分布
下面是C/C++中程序内存区域划分图,小编对它们的分配进行解读:

**内核空间:**用户代码不能读写,一般用于操作系统内核使用
栈:向下增长,用于存储局部变量和函数调用信息(适合临时数据)
内存映射段:文件映射、动态库、匿名映射(后面再学,当前不易掌握)
堆:向上增长,用于动态内存分配(提供灵活的内存大小,但空间有限)
数据段:存储全局变量和静态数据(数据需要长期存在)
代码段:存放可执行代码和只读常量(保护代码和常量不被修改)
数据分配解读:
(1)图中的全局变量、静态数据需要长期存在,程序启动时分配,程序结束时释放
(2) localVar、num1[10]、char2[ ]、pChar3、ptr1、ptr2、ptr3这些变量在函数调用时分配,函 数结束时自动释放,随着函数开辟,因此存储在栈区
(3)pChar3指针指向的"abcd"属于字符串、char2的字符数组,属于只读类,因此存储在代码段
(4)malloc、realloc、calloc开辟的内存空间不受函数控制,需要手动释放,固在堆区
问:为什么指针、以及对应后面的数据需要分开存储?(雷区:误以为整行语句在同一个空间)
(1)首先指针本身就是一个变量(指针变量),存储指向后面数据的地址,它的生命周期遵循局 部变量规则,固存储在栈区
(2)同理 num1【】是一个局部数组变量,它的生命周期也是随当前函数开辟释放
(3)而字符指针指向的字符串数据内容属于只读需要,固存储在代码段更好
**总结:**通过高效的内存分配方式,不断提高程序的性能和安全性
内存分布测试
cpp
(1)static int staticGlobalVar = 1;
int main()
{
(2)int globalVar = 1;
(3)static int staticVar = 1;
(4)int localVar = 1;
(5)int num1[10] = { 1, 2, 3, 4 };
(6)char char2[] = "abcd";
(7)const char* pChar3 = "abcd";
(8)int* ptr1 = (int*)malloc(sizeof(int) * 4);
(9)int* ptr2 = (int*)calloc(4, sizeof(int));
(10)int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
(11)free(ptr1);
(12)free(ptr3);
return 0;
}
A:栈 B:堆 C:数据段(静态区) D:代码段(常量区)
globalVar在哪------A staticGlobalVar在哪------C
staticVar在哪------C localVar在哪------A
num1 在哪------A char2在哪------A
*char2在哪------D pChar3在哪------A
*pChar3在哪------D ptr1在哪------A
*ptr1在哪------B
C语言内存管理
首先C语言是通过四个函数来实现动态内存管理的,它们的作用如下:
**malloc:**分配指定字节数未初始化内存块
cpp
int* ptr1 = (int*)malloc(sizeof(int) * 4);
**realloc:**调整原来开辟的空间,参数为原地址起始地、理想总空间大小
cpp
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
**calloc:**分配指定数量和大小的内存块,并初始化为0
cpp
int* ptr2 = (int*)calloc(4, sizeof(int));
**free:**释放malloc、calloc、realloc开辟的内存空间,防止内存泄漏,参数是释放前空间起始地址
cpp
free(ptr1);
C++内存管理
首先C++兼容C语言,C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦。因此C++又提出了自己的内存管理方式:
通过new和delete操作符进行动态内存管理(记住是操作符,不是函数哦!)
使用

C++内存开辟对比C语言更加简洁了,不用去繁琐的判断,例如:
cpp
//申请一个int类型大小的空间
int* ptr4 = new int;
//申请一个int类型大小的空间并初始化为10
int* ptr5 = new int(10);
//申请十个int类型大小的空间
int* ptr6 = new int[10];
//释放
delete ptr4;
delete ptr5;
delete[] ptr6;

外围用try捕获异常:
cpp
try
{
Stack* pc = new Stack;
}
catch (const exception& e)
{
//捕获信息反馈(可以是打印)
}
注意:开辟释放需要配套(开辟+捕获)使用,如果不加捕获,程序会直接崩溃,否则容易出问题
例如:单个空间申请用单个的形式释放;如果是new【】类型的申请,需要用delete【】释放
优势
malloc与free 较于 new与delete 的最大区别是自定义类型的申请开辟(内置类型几乎一致)
malloc对于自定义类型
简单申请一个一定大小的空间就可以了,对于空间需要判断有效性,其它好像也没哈......
new对于自定义类型
new对于自定义类型可以调用构造函数,delete释放内存可以调用析构函数
例如:

效果展示:

operator new与operator delete函数
介绍
new 和 delete是用户进行动态内存申请和释放的操作符,operator new 和 operator delete是系统提供的 全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局 函数来释放空间,下面的底层实现了解即可,这对于我们掌握内存开辟是很有用的!
operator new的底层实现

可以看到 operator new 的底层是 malloc实现的, 它较于 malloc 很大的不同是:
如果开辟空间失败,那么就会抛出异常,而 malloc 则会返回空地址
operator delete的底层实现
可以看到尽管再怎么去修饰,它的底层还是free函数~
总结
通过上述两个全局函数的实现我们可以看到:
operator new实际上也是通过malloc函数来实现,只是二者在空间"报错"上有了明显区别
operator delete实际上也是通过free函数来实现
new与delete的实现原理
内置类型
对于 int、char、double、long........这些内置类型
如果申请的内置类型的空间,new与malloc基本类似,二者不同地方在于:
new与delete申请/释放的是单个元素的空间,new[ ]与delete[ ]申请的是连续地址的空间
而new申请失败会抛出异常,malloc开辟失败会返回空(无效地址)
自定义类型
new的原理
(1)先调用operator new函数申请空间
(2)在申请的空间上执行构造函数,完成对象的创建

delete的原理
(1)调用析构函数,完成对象中资源的清理工作
(2)调用operator delete函数,完成对空间的释放

举例
申请一个在堆上的栈对象
这里的需求完美与我们的new开辟匹配
(1)先调用operator new函数在堆上开辟内存
(2)再调用构造函数在栈上开辟对象
cpp
Stack* pc = new Stack;

new[ ]的原理
对于有多个数据的申请:
(1)调用N次operator new函数完成空间开辟
(2)再调用N次构造函数执行构造
delete[ ]的原理
(1)调用N次析构函数完成资源的清理
(2)调用N次operator delete函数完成空间释放
常见面试题总结
malloc/free与new/delete区别
共同特点
都需要手动开辟、手动释放
直接特点
(1)malloc和free是函数,new和delete是操作符(关键字)
(2)malloc开辟需要指定空间大小,new开辟只需要类型(多个空间需要指定个数)
(3)malloc开辟不能初始化,new开辟可以
(4)malloc开辟空间需要强转类型,new开辟只需要指明类型即可
(5)malloc申请失败返回的是NULL,new开辟失败会捕获异常
底层特点
malloc开辟只是简单的申请对应大小的空间,free也是直接释放空间
new开辟会调用operator new、构造函数(先开辟空间,再进行构造)
delete释放会调用operator delete、析构函数(先清理资源,再释放空间)
内存泄漏
什么是内存泄漏
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。
内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费
内存泄漏危害
长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死
内存泄漏分类
堆内存泄漏:
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存, 用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那 么以后这部分空间将无法再被使用,就会产生Heap Leak
系统资源泄漏:
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统 资源的浪费,严重可导致系统效能减少,系统执行不稳定
如何检查/避免内存泄漏
(1)首先是检查内存泄漏,这里先只了解,后面会进行学习
(2)良好的操作习惯、智能指针(后面学习)、某些公司会自带内存泄漏检测功能、内存泄漏检 测工具(昂贵)

【雾非雾】期待与你的下次相遇!