【C/C++内存管理】——我与C++的不解之缘(六)

张开发
2026/4/14 22:03:03 15 分钟阅读

分享文章

【C/C++内存管理】——我与C++的不解之缘(六)
前言最近开学了更新有些迟缓了现在来学C/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); }对于以上代码这些创建的全局变量局部变量以及静态变量等都分别存放在内存的哪些区域说明1、栈又叫做堆栈 -- 非静态局部变量、函数参数和返回值等栈是向下增长的。2、堆用于程序运行时动态内存分配堆是向上增长的。3、数据段静态区-- 存储全局数据和静态数据。4、代码段常量区-- 可执行的代码/只读常量比如上述的常量字符串abcd。5、内存映射段 是高效的 I/O映射方式用于装载一个共享的动态内存库用户可以使用接口创建共享内存做进程间通信后面再学习这一块的知识。二、C语言中动态内存管理方式在C语言中学习过C语言的动态内存管理方式malloc /calloc /realloc /free这里就不过多的讲解如有遗忘就去重温一下C语言——动态内存管理三、C中内存管理方式由于C是兼容C语言的所以C语言的内存管理方式在C当中也可以继续使用但是在一些方面C语言的malloc /calloc /realloc /free使用起来就比较麻烦因此C提出了自己的内存管理方式通过 new和 delete 操作符进行动态内存管理。3.1、new / delete 操作内置类型void test() { //动态申请一个int大小的空间 int* p1 new int; //动态申请一个int大小的空间并初始化为520; int* p2 new int(520); //动态申请十个int大小的空间(数组) int* p3 new int[10]; //动态申请十个int大小的空间(数组),并初始化 int* p4 new int[10] {1, 2, 3, 4, 5, 6, 7, 8, 9}; delete p1; delete p2; //释放数组(多个内置类型) delete[] p3; delete[] p4; }注意申请和释放单个元素的空间使用new和delete操作符申请和释放连续的空间使用new[ ]和delete[ ]。配套使用3.2、new和delete操作自定义类型对于内置类型new/delete 和malloc/free 差别不是很大而对于自定义类型最大的区别就是new和delete除了会开辟空间还会调用自定义类型的构造函数和析构函数。class A { public: A(int a 1) :_a(a) { std::cout A() std::endl; } ~A() { std::cout ~A() std::endl; } private: int _a; }; int main() { A* p1 (A*)malloc(sizeof(A)); free(p1); A* p2 new A; delete p2; return 0; }可以看到malloc和free并没有调用构造函数和析构函数这里new和delete调用了自定义类型的构造函数和析构函数。四、operator new与operator delete函数4.1、operator new 和operator delete 函数new和delete是我们用户进行动态内存申请和释放的操作符operator new和operator delete 是系统提供的全局函数new在底层是调用operator new全局函数来申请空间delete在底层通过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实际上也是通过malloc来申请空间如果申请成功就直接返回如果失败就执行用户通过的空间不足应对措施如果用户没有提供就抛异常。operator delete最终是通过free来释放空间的。4.2、抛异常这里简单使用一下抛异常和捕获异常抛异常也是new和malloc的区别之一先看一下代码这里在x8632位环境下动态开辟空间直到申请失败抛异常#includeiostream int main() { int* p; do { p new int[1024 * 1024]; std::cout p std::endl; } while (p); return 0; }这里提示未经处理的异常说明new动态申请空间失败了抛异常。现在使用try catch捕获异常并输出。#includeiostream #includeexception int main() { try { int* p; do { p new int[1024 * 1024]; std::cout p std::endl; } while (p); } catch (const std::exception e) { std::cout e.what() std::endl; } return 0; }五、new 和delete 的实现原理5.1、内置类型如果动态申请内置类型的空间new和malloc、delete和free 基本相似不同的是1、new/delete申请和释放的是单个元素的空间new[ ] 和delete[ ] 申请和释放的是连续的空间。2、new在申请空间失败会抛异常而malloc会返回NULL。5.2、自定义类型5.2.1、new的原理1、先调用operator new 函数申请空间。2、在申请的空间上调用自定义类型的构造函数完成对象的构造。5.2.2、delete的原理1、在空间上调用自定义类型的析构函数完成对象中资源的清理2、调用operator delete 函数释放空间。5.2.3、new T[N] 的原理1、调用operator new[ ]函数在operator中实际调用了operator new 完成N个对象的申请2、在申请的空间上调用N次构造函数。5.2.4、delete[ ] 的原理1、在空间上调用N次析构函数完成N个对象中资源的清理。2、调用operator delete[ ]函数实际在operator delete[ ]中调用operator delete来释放空间。六、定位 new 表达式placement - new所谓定位new 表达式是在已经申请好的内存空间中调用构造函数初始化一个对象。使用方法new(place_address)type 或者 new(place_address)type(initializer-list)place_address必须是一个指针initializer-list是类型的初始化列表使用场景一般来说是配合内存池使用。因为内存池分配出来的内存没有初始化所以如果是自定义类型的对象就需要使用new的定义表达式进行显示的调用构造函数进行初始化。#includeiostream class A { public: A(int a 1) :_a(a) { std::cout A() std::endl; } ~A() { std::cout ~A() std::endl; } private: int _a; }; int main() { //动态申请空间malloc不会初始化 A* p1 (A*)malloc(sizeof(A)); //new表达式显示调用构造函数 new(p1)A(99); //显示调用析构函数 p1-~A(); //释放空间 free(p1); return 0; }七、malloc/free 和 new/dalete的区别首先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在释放空间前会调用析构函数完成空间中资源的清理。八、内存泄露在C语言内存管理中提到过内存泄露这里简单理解一下在之后学习中会深入学习内存泄露以及避免内存泄露。内存泄露内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失而是应用程序分配某段内存后因为设计错误失去了对该段内存的控制因而造成了内存的浪费。内存泄漏的危害:长期运行的程序出现内存泄漏影响很大如操作系统、后台服务等等出现内存泄漏会导致响应越来越慢最终卡死。感谢各位大佬支持并指出问题如果本篇内容对你有帮助可以一键三连支持以下感谢支持

更多文章