
本篇文章主要介绍在C语言的内存管理的基础上C的内存管理方式1.C\C 内存分布C和C内存分布主要是 栈堆静态区数据段常量区代码段还有一块内存映射段。各个区别如下栈Stack由操作系统自动管理自动分配、自动释放不需要手动申请释放。存放函数局部变量、函数形参、函数返回地址、临时变量。栈空间很小一般几 MB生长方向向下向低地址增长。函数调用结束栈帧直接销毁局部变量立刻失效。栈内存不会产生内存泄漏但容易栈溢出比如递归过深、定义超大局部数组。堆Heap由程序员手动管理C 用malloc/freeC 用new/delete。需要手动申请内存使用完必须手动释放否则发生内存泄漏。堆空间很大远大于栈生长方向向上向高地址增长。用于存放程序运行时才确定大小的数据动态开辟的对象、数组都放在堆上。堆内存释放后要避免野指针问题。静态区数据段分为初始化数据段、未初始化数据段 (BSS 段)。存放全局变量、static 静态变量。程序启动时分配程序结束才释放生命周期是整个程序运行周期。 初始化数据段存放已经初始化的全局变量、static 静态变量 BSS 段存放未初始化的全局变量、静态变量程序加载时系统自动置 0。常量区 / 代码段Text 段代码段存放程序编译后的机器指令二进制代码常量区一般附属在代码段存放字符串字面量、const修饰的全局常量。该区域内存只读不允许修改尝试写操作会触发程序崩溃。生命周期贯穿整个程序程序退出由系统回收。注意局部 const 变量不在常量区在栈上。内存映射段mmap 映射区位于堆和栈之间。用来做文件映射、动态链接库.so 加载、匿名内存映射。操作系统把磁盘文件直接映射到虚拟内存程序可以直接像访问内存一样读写文件不需要频繁 read/write 系统调用。也用于大内存分配当 malloc 申请超大块内存时系统不走堆直接使用 mmap 开辟。这部分内存由操作系统管理。下面我们结合代码和图片来理解一下2.C语言中动态内存管理方式malloc/calloc/realloc/freemalloc申请 size 字节堆内存内存不初始化。void* malloc(size_t size);calloc申请 num 个、每个 size 字节的堆内存分配后自动置 0。void* calloc(size_t num, size_t size);realloc重新修改已有堆内存的大小可以扩容也可以缩容不一定在原地址操作。void* realloc(void* ptr, size_t new_size);free:就是把开辟的空间归还给操作系统。3.C内存管理方式C语言内存管理方式在C中可以继续使用但有些地方就无能为力而且使用起来比较麻烦因此C又提出了自己的内存管理方式通过new和delete操作符进行动态内存管理。3.1new/delete操作内置类型void Test() { // 动态申请一个int类型的空间 int* ptr4 new int; // 动态申请一个int类型的空间并初始化为10 int* ptr5 new int(10); // 动态申请10个int类型的空间 int* ptr6 new int[3]; delete ptr4; delete ptr5; delete[] ptr6; }注意申请和释放单个元素的空间使用new和delete操作符申请和释放连续的空间使用new[]和delete[]注意匹配起来使用。3.2new和delete操作自定义类型使用new 为自定义类型开辟空间的方法和内置类型一样delete 释放空间的使用方法也是一样的但是和内置类型的区别是new为自定义类型开辟空间后会调用构造函数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/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间 //还会调用构造函数和析构函数 A* p1 (A*)malloc(sizeof(A)); A* p2 new A(1); free(p1); delete p2; // 内置类型是几乎是一样的 int* p3 (int*)malloc(sizeof(int)); int* p4 new int; free(p3); delete p4; A* p5 (A*)malloc(sizeof(A) * 10); A* p6 new A[10]; free(p5); delete[] p6; return 0; }4.operator new与operator delete函数new和delete是用户进行动态内存申请和释放的操作符operator new和operator delete是系统提供的全局函数new在底层调用operator new全局函数来申请空间delete在底层通过operator delete全局函数来释放空间。其次就是operator new实际也是通过malloc来申请空间如果 malloc申请空间成功就直接返回否则执行用户提供的空间不足应对措施如果用户提供该措施 就继续申请否则就抛异常。operator delete最终是通过free来释放空间的。这里主要是说明new在开辟空间后不需要像malloc那样检查是否为空只要捕获异常即可。5.new和delete的实现原理5.1内置类型如果申请的是内置类型的空间new和mallocdelete和free基本类似不同的地方是new/delete申请和释放的是单个元素的空间new[]和delete[]申请的是连续空间而且new在申请空间失败时会抛异常malloc会返回NULL。5.2自定义类型new原理1.调用operator new函数申请空间2.在申请的空间上执行构造函数完成对象的构造delete原理1.在空间上执行析构函数完成对象中资源的清理工作2.调用operator delete函数释放对象的空间new[ N ]原理1.调用operatornew[]函数在operatornew[]中实际调用operatornew函数完成N个对象空间的申请2.在申请的空间上执行N次构造函数delete[ N ] 原理1.在释放的对象空间上执行N次析构函数完成N个对象中资源的清理2.调用operator delete[]释放空间实际在operator delete[]中调用operator delete来释放空间6.定位new表达式定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。使用格式new (place_address) type或者new (place_address) type(initializer-list)place_address必须是一个指针initializer-list是类型的初始化列表比如下面的程序class A { public: A(int a 0) : _a(a) { cout A(): this endl; } ~A() { cout ~A(): this endl; } private: int _a; }; // 定位new/replacement 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; }定位new表达式使用较多的场景是配合内存池使用因为内存池分配出的内存没有初始化所以如果是自定义类型的对象需要使用new的定义表达式进行显示调构造函数进行初始化。7.malloc/free和new/delete的区别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在释放空间前会调用析构函数完成空间中资源的清理释放