C++ 内存管理原理分析 | 您所在的位置:网站首页 › c内存分布图 › C++ 内存管理原理分析 |
目录1.C/C++中程序内存分布1.1 内存分布图1.2 小试牛刀2.C语言部分的动态内存管理方式3.C++内存管理方式3.1new/delete操作内置类型3.2 new/delete操作自定义类型4.new和delete底层实现原理(important!!!)4.1operator new/operator delete4.2new和delete的实现原理内置类型自定义类型5.相关面经5.1malloc/free与new/delete的区别5.2什么是内存泄漏?5.3内存泄漏的危害5.4如何预防内存泄漏(先了解一下,后续作者再详细介绍)1.C/C++中程序内存分布
C/C++中程序内存区域大致划分为:内核空间(这部分用户不能读写)、栈、内存映射段、堆、数据段(存储全局数据、静态数据)、代码段(存储可执行代码、只读常量,又称常量区)。 1.1 内存分布图 1.2 小试牛刀接下来看下如下代码,思考下每一个变量分别在哪个内存区域? 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";//有的编译器会报错,需要用const char 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(ptr2); }上述代码段对应变量区域划分如下: 2.C语言部分的动态内存管理方式再来回顾一下之前C语言部分的动态内存管理方式:malloc / calloc/ realloc和free 带着两个问题阅读下述程序段: 1.malloc / calloc/ realloc的区别是什么? 2.最后需要free(p2)吗? void Test() { int* p1 = (int*)malloc(sizeof(int)); free(p1); int* p2 = (int*)calloc(4, sizeof(int)); int* p3 = (int*)realloc(p2, sizeof(int) * 10); free(p3); }答: 1.calloc相当于malloc+memset(0),即开空间+初始化。 2.realloc是对malloc/calloc的空间进行扩容,扩容之下又涉及到了咱们前面所讲的原地扩容和异地扩容俩种情景:原地扩容p2和p3是一样的,也有可能是异地扩容,那么p2指向的空间已经被释放了,所以两种情况下我们都可以不需要处理p2。 3.C++内存管理方式总之就是C语言那套内存管理方式相对麻烦,所以C++提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理. 3.1new/delete操作内置类型1.开辟单个元素 开辟单个元素基本语法: type * ptr = new type(content); ,可以理解成动态申请一个type类型的空间并将其中内容初始化为content,当然,你也可以选择不初始化。 释放空间语法: delete name; 例: int* a = new int(100); //动态申请一个int类型的空间并初始化为100 delete a;2.开辟n个type类型的空间 开辟n个type类型基本语法: type* name = new type[n] 删除的基本语法:delete[] name; 例: int* ptr = new int[100];//动态申请100个int类型的空间 delete[] ptr; //注意哦!一定要匹配哦!不然必崩溃!3.对于内置类型,malloc/free和new/delete确实没有什么区别,二者的作用完全一样。 例: int main() { //malloc向内存申请4个整型大小的空间 int* p1 = (int*)malloc(sizeof(int) * 4); //new向内存申请4个整型大小的空间 int* p2 = new int[4]; //free释放掉p1申请的空间 free(p1); p1 = nullptr; //delete释放掉p2申请的空间 delete[] p2; return 0; } 3.2 new/delete操作自定义类型 class Date { public: Date(int year=2021, int month=1, int day=1) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; int main() { //malloc申请十个Date空间 Date* p1 = (Date*)malloc(sizeof(Date) * 10); free(p1); //new申请十个Date空间 Date* p2 = new Date[10]; delete[] p2; return 0; }区别:在申请自定义类型空间的时候,new会调用构造函数,delete会调用析构函数,而mallo和free不会哦! 4.new和delete底层实现原理(important!!!)在讲解他们的底层实现原理之前需要先先介绍一下两个全局函数,分别是operator new 和operator delete. new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过调用operator delete全局函数来释放空间。 4.1operator new/operator deleteoperator new封装了 malloc 和失败抛异常俩个部分, 下面是operator new的代码: 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 下面是operator delete的代码: 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; }总结:通过观察上述俩个全局函数的实现,不难发现operator new实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常,operator delete最终是通过free来释放空间的。 4.2new和delete的实现原理 内置类型malloc/free与new/delete在处理内置类型时并没有区别,只是malloc申请空间失败时返回空指针,而new申请空间时是抛异常,new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间。 自定义类型1.new的原理:1.调用operator new函数申请空间。2.在申请空间上执行构造函数,完成对象的初始化。 2.delete的原理:1.在空间上执行析构函数,完成对象中资源的清理工作。2.调用operator delete函数释放空间。 另外new T[N]的原理:调用operator new[]函数,在operator new[]中实际调用N次operator new函数完成N个对象空间的申请,然后在申请的空间上执行N次构造函数。**delete[]的原理:**在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理。然后调用operator delete[]释放空间,实际在operator delete[]中调用N次operator delete来释放空间。 初学者看到“delete调用析构函数,完成对象资源的清理工作,后边又调用operator delete函数释放空间”这部分内容时可能会比较混乱,这里以栈为例子详细说下: struct Stack { int* _a; int _top; int _capacity; Stack(int capacity = 4) :_a(new int[capacity]) ,_size(0) ,_capacity(capacity) { cout |
CopyRight 2018-2019 实验室设备网 版权所有 |