一、C/C内存分布先让我们先看一看以下的代码intglobalVar1;staticintstaticGlobalVar1;voidTest(){staticintstaticVar1;intlocalVar1;intnum1[10]{1,2,3,4};charchar2[]abcd;constchar*pChar3abcd;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. 选择题//选项 : A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)////globalVar在哪里____ C 全局变量未被 static 修饰但作用域是整个程序存放在数据段静态区//staticGlobalVar在哪里____ C static 修饰的全局变量作用域限制在当前文件但存储位置仍在数据段//staticVar在哪里____ C 函数内 static 变量生命周期全局存储在数据段仅作用域在函数内//localVar在哪里____ A 函数内普通局部变量栈帧分配函数结束后销毁//num1 在哪里____ A 函数内数组属于局部变量数组本身存储空间在栈上//char2在哪里____ A 函数内字符数组数组名是栈上的地址存储在栈//* char2在哪里___ A char2 是数组首地址*char2 是数组第一个元素 a元素都存在栈上的数组空间里//pChar3在哪里____ A 函数内的指针变量本身存储地址的变量在栈上//* pChar3在哪里____ D 指针指向字符串常量 abcd常量存储在只读的代码段常量区//ptr1在哪里____ A 指针变量本身存储 malloc 返回地址的变量在栈上//* ptr1在哪里____ B ptr1 指向 malloc 申请的内存动态内存分配在堆上说明栈又叫堆栈——非静态局部变量/函数参数/返回值等等栈是向下增长的。内存映射段是高效的I/O映射方式用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存做进程间通信。Linux堆用于程序运行时动态内存分配堆是可以上增长的。数据段——存储全局数据和静态数据。代码段——可执行的代码/只读常量。注意calloc/realloc 和 malloc 一样申请的内存都在堆区二、C语言中的动态内存管理方式calloc/realloc/malloc/freecalloc / realloc / malloc的区别malloc主要用于分配指定字节大小的内存块它仅完成内存分配操作不会对分配的内存做任何初始化处理内存中的内容是随机的垃圾值使用时只需传入一个表示总字节数的参数即可适合对性能要求高、无需初始化的小块内存分配场景。calloc同样是分配内存但它会在分配后自动将内存内容初始化为 0安全性更高使用时需要传入两个参数分别是元素个数和单个元素的大小更适合需要内存清零、对数据安全性有要求的场景。realloc核心作用是调整已通过 malloc/calloc 分配的内存块大小它的参数包含原内存指针和调整后的总大小调整后的新增内存部分不会被初始化当 realloc 执行时若原内存块后方有足够连续空间会直接扩展内存(原地扩容)若空间不足则会申请新的内存块、拷贝旧数据并自动释放原内存块异地扩容若 realloc 调用失败会返回 NULL此时原内存块依然有效不会被释放。malloc的实现原理glibc中的malloc实现原理Linux三、C内存管理方式注意C语言内存管理方式在C中可以继续使用但有些地方就无能为力而且使用起来比较麻烦因此C又提出了自己的内存管理方式通过 new 和 delete 操作符进行动态内存管理。new/delete操作内置类型voidTest(){// 动态申请一个int类型的空间int*ptr1newint;int*ptr2newint[10];deleteptr1;delete[]ptr2;// 支持初始化int*ptr3newint(0);int*ptr4newint[10]{1,2,3,4};deleteptr3;delete[]ptr4;}总结new/delete是 C 面向对象的内存管理方式支持初始化和构造 / 析构函数调用malloc/free是 C 的底层内存函数仅做内存分配释放使用规则new配delete、new[]配delete[ ]不可混用避免内存泄漏或析构不全内存分布new/malloc分配的内存在堆区指针变量本身如 ptr4/ptr5/ptr6在栈区。new和delete操作自定义类型intmain(){// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数A*p1(A*)malloc(sizeof(A));A*p2newA;// 调用默认构造A*p3newA(10);A*p4newA[5];free(p1);deletep2;deletep3;delete[]p4;return0;}new/delete 与 malloc/free 核心区别malloc/free 仅做内存操作不会调用A的构造函数new/delete 完整管理对象生命周期new A先调用operator new底层封装malloc分配内存再自动调用A的默认构造函数将原始内存初始化为合法的A对象new A(10)分配内存后调用A的带参构造函数完成对象初始化delete p2/delete p3先调用A的析构函数释放对象持有的资源再调用operator delete底层封装free释放内存。new [ ] / delete [ ] 的底层实现原理当用new A[5]创建自定义类型数组时new[]并非只分配5*sizeof(A)的内存而是会在数组内存的 “头部” 额外开辟 4/8 字节取决于系统位数的空间用于存储数组的元素个数这里是 5。new [ ] 会多开辟 “计数内存”[计数区(存储5)] [A对象1] [A对象2] … [A对象5]返回的指针p4指向第一个A对象的起始地址而非计数区对用户透明。delete [ ] 的执行逻辑为何不能拆分成多个 deletedelete[ ] p4 执行时会先根据p4回退到计数区读取元素个数5从最后一个A对象开始依次调用 5 次A的析构函数保证每个对象的资源都被释放最后释放包含计数区在内的全部内存调用operator delete[ ]。四、operator new与operator delete函数重要点进行讲解operator new与operator delete函数重点注意new和delete是用户进行动态内存申请和释放的操作符operator new和operator delete是系统提供的全局函数new 在底层调用operator new全局函数来申请空间delete 在底层通过operator delete全局函数来释放空间。operator new 核心概念本质C 提供的全局函数也可类内重载核心职责是仅分配堆内存不涉及任何对象的构造逻辑。函数原型默认全局版void* operator new(size_t size);参数 size需要分配的内存字节数返回值成功返回指向分配内存的 void* 指针失败默认抛出 std::bad_alloc 异常而非像 malloc 那样返回 NULL。底层逻辑默认实现是对 malloc 的封装会循环尝试调用 malloc 申请内存若 malloc 失败会调用内存不足处理函数仍失败则抛异常异常章节学习。operator delete 核心概念本质C 提供的全局函数也可类内重载核心职责是仅释放堆内存不涉及任何对象的析构逻辑。函数原型默认全局版void operator delete(void* ptr);参数 ptr需要释放的内存指针若为 NULL函数无操作无返回值。底层逻辑默认实现是对 free 的封装释放前会做指针有效性校验保证线程安全后调用 free 释放内存。重载特性可在全局范围或类内重载 operator new/operator delete实现自定义内存管理策略如内存池、内存对齐、内存使用统计等类内重载时该类的 new 操作会优先调用自定义版本全局重载则影响所有未自定义的类。代码示例#includeiostream#includemalloc.h// malloc/free#includenew// bad_allocusingnamespacestd;// 1. 全局重载 operator new/operator delete // 自定义全局 operator new封装 malloc添加分配日志void*operatornew(size_t size){cout[全局 operator new] 申请 size 字节内存endl;void*ptrmalloc(size);// 底层调用 mallocif(!ptr){// 模拟内存不足场景throwbad_alloc();// 失败抛异常}returnptr;}// 自定义全局 operator delete封装 free添加释放日志voidoperatordelete(void*ptr)noexcept{cout[全局 operator delete] 释放内存ptrendl;if(ptr){free(ptr);// 底层调用 free}}// 2. 类内重载 operator new/operator delete classMyClass{public:intnum;// 类内重载 operator new仅为该类分配内存添加专属日志void*operatornew(size_t size){cout[MyClass operator new] 为MyClass申请 size 字节内存endl;void*ptrmalloc(size);if(!ptr){throwbad_alloc();}returnptr;}// 类内重载 operator delete仅释放该类的内存voidoperatordelete(void*ptr)noexcept{cout[MyClass operator delete] 释放MyClass内存ptrendl;if(ptr){free(ptr);}}// 构造函数仅初始化不涉及内存分配MyClass(intn0):num(n){coutMyClass 构造函数num numendl;}// 析构函数仅清理不涉及内存释放~MyClass(){coutMyClass 析构函数num numendl;}};// 3. 测试逻辑 intmain(){try{// 测试1基础类型使用全局重载的 operator new/deleteint*p1newint(10);// new运算符调用全局operator new 初始化coutp1 指向的值*p1endl;deletep1;// delete运算符调用全局operator deletecout------------------------endl;// 测试2自定义类使用类内重载的 operator new/deleteMyClass*p2newMyClass(20);// new运算符调用MyClass::operator new 构造函数coutp2-nump2-numendl;deletep2;// delete运算符调用析构函数 MyClass::operator delete}catch(constbad_alloce){// 捕获内存分配失败的异常cerr内存分配失败e.what()endl;return1;}return0;}总结通过上述两个全局函数的实现知道operatornew实际也是通过malloc来申请空间如果malloc申请空间成功就直接返回否则执行用户提供的空间不足应对措施如果用户提供该措施就继续申请否则就抛异常。operatordelete最终是通过free来释放空间的。五、 定位new表达式(placement-new)定位 newplacement new是一种特殊的 new 表达式它不在堆上申请新的内存而是在一块已经分配好的、原始的内存地址上直接调用构造函数来初始化一个对象。核心作用在已分配的内存空间中 “就地” 构造对象常用于内存池、自定义内存管理等场景。使用格式new(place_address)type;new(place_address)type(initializer-list);//place_address必须是一个合法的指针指向一块大小足够、且未被使用的内存。//type要构造的对象类型。//initializer-list对象的初始化参数用于调用对应的构造函数。注意通过定位 new 构造的对象不能直接用 delete 释放必须显式调用其析构函数然后再释放原始内存。代码示例classA{public:A(inta0):_a(a){coutA() 构造函数: this this, _a _aendl;}~A(){cout~A() 析构函数: this this, _a _aendl;}intGetA()const{return_a;}private:int_a;};intmain(){// 场景1在 malloc 分配的内存上使用定位 newcout 场景1在 malloc 分配的内存上使用定位 new endl;// 1. 先分配原始内存A*p1(A*)malloc(sizeof(A));if(p1nullptr){cerrmalloc 失败endl;return1;}coutmalloc 分配的内存地址: p1endl;// 2. 使用定位 new 在 p1 指向的内存上构造对象 A(10)new(p1)A(10);cout对象 p1 的值: p1-GetA()endl;// 3. 显式调用析构函数清理对象p1-~A();// 4. 释放原始内存free(p1);cout\n 场景2在 operator new 分配的内存上使用定位 new endl;// 1. 先通过 operator new 分配原始内存A*p2(A*)operatornew(sizeof(A));coutoperator new 分配的内存地址: p2endl;// 2. 使用定位 new 在 p2 指向的内存上构造对象 A(20)new(p2)A(20);cout对象 p2 的值: p2-GetA()endl;// 3. 显式调用析构函数p2-~A();// 4. 释放原始内存operatordelete(p2);return0;}六、malloc / free和new / delete的区别malloc / free和new / delete的共同点是都是从堆上申请空间并且需要用户手动释放。不同的地方是malloc和free是函数new和delete是操作符malloc申请的空间不会初始化new可以初始化malloc申请空间时需要手动计算空间大小并传递new只需在其后跟上空间的类型即可如果是多个对象[]中指定对象个数即可malloc的返回值为void*, 在使用时必须强转new不需要因为new后跟的是空间的类型。malloc申请空间失败时返回的是NULL因此使用时必须判空new不需要但是new需要捕获异常。申请自定义类型对象时malloc/free只会开辟空间不会调用构造函数与析构函数而new在申请空间后会调用构造函数完成对象的初始化delete在释放空间前会调用析构函数完成空间中资源的清理释放。