公司动态

CC++内存管理

📅 2026/7/15 21:11:20
CC++内存管理
文章目录CC内存管理1. C/C内存分布2. C语言中动态内存管理方式3. C中动态内存管理4. operator new与operator delete5. new 和 delete 的实现原理内置类型自定义类型6. 定位new表达式(placement-new)CC内存管理1. C/C内存分布一个运行中的C/C程序其虚拟地址空间通常由操作系统组织成以下区域从高地址到低地址或反之不同OS有差异但逻辑分区一致他们在代码中的表示#includestdio.hintglobal_init_var84;// .data段intglobal_uninit_var;// .bss段 (未初始化)staticintstatic_var85;// .data段 (静态变量)intmain(){staticintlocal_static86;// .data段生命周期贯穿程序intlocal_var87;// 栈int*heap_var(int*)malloc(sizeof(int));// heap_var本身在栈指向堆内存constchar*strHello;// str在栈指向的字符串Hello在代码段(.rodata)printf(代码段地址: %p\n,(void*)main);printf(数据段(.data)地址: %p\n,global_init_var);printf(数据段(.bss)地址: %p\n,global_uninit_var);printf(栈地址: %p\n,local_var);printf(堆地址: %p\n,heap_var);free(heap_var);return0;}2. C语言中动态内存管理方式C语言的内存管理提供了四个函数定义在 stdlib.h 中它们操作的是原始内存void* malloc(size_t size)在堆上分配size字节的未初始化内存。成功返回指向分配内存起始地址的指针void*失败返回NULL。使用前必须强制类型转换intp (int)malloc(sizeof(int));void* calloc(size_t n, size_t size)分配n * size字节的内存并将每一位初始化为0。适用于数组分配intarr (int)calloc(10, sizeof(int));void* realloc(void* ptr,size_t new_size)调整之前已分配内存块的大小。如果新大小小于原大小可能截断。如果原块后有空闲空间直接扩展否则会新分配一块更大的内存将原数据拷贝过去自动释放原内存并返回新地址。如果ptr为NULL行为等同于malloc。如果new_size为0行为是实现定义的通常不建议依赖。void* free(void* ptr)释放ptr指向的内存。如果ptr是NULL不做任何事。释放后指针本身成为了“悬空指针”必须立即置为NULL否则会引发难以追踪的UAFUse After Free错误。C语言通过上述函数来向内核去申请内存自由度高但是这种自由度往往是由程序员自律管理因为C语言内存管理可以认为是一种契约完全依赖契约者(程序员)的“诚信”如果契约者(程序员)不遵守契约或者忘记了这对于跑程序的计算机来说是毁灭性的。3. C中动态内存管理C提供了操作符new/delete 和 new[]/ delete[]核心改进是将内存分配与对象构造/析构绑定在一起new 与 new[]voidtest1(){Date*arr1newDate(10);Date*arr2newDate[10];}delete 与 delete[]voidtest1(){Date*arr1newDate(10);Date*arr2newDate[10];deletearr1;delete[]arr2;}new只能用delete而new[]也只能用delete[] 来进行释放如果new/new[]完不去delete/delete[]去释放那么就会发生内存泄漏的问题。相比C的先申请内存再去初始化。new/delete更安全且更方便这个更安全和更方便我们后面再讲。但也不能说new/delete就没有缺点它和C语言的free一样必须程序员手动的去释放因为离开作用域不会自动析构所以new/delete是一个半自动的。4. operator new与operator deletenew和delete是用户进行动态内存申请和释放的操作符operator new 和operator delete是系统提供的全局函数new在底层调用operator new全局函数来申请空间delete在底层通过operator delete全局函数来释放空间。// operator new__attribute__((__weak__,__visibility__(default)))void*operatornew(std::size_t size){if(size0)// 处理 size 为 0 的边界情况size1;// 确保至少分配 1 字节void*p;while((pstd::malloc(size))0){// 核心调用 mallocstd::new_handler nhstd::get_new_handler();if(nh)nh();// 调用用户自定义的 new_handler 尝试释放内存elsethrowstd::bad_alloc();// 无 handler 可用抛出异常}returnp;}// operator delete__attribute__((__weak__,__visibility__(default)))voidoperatordelete(void*ptr){if(ptr)// 处理空指针std::free(ptr);// 核心调用 free}// free的实现#definefree(p)_free_dbg(p,_NORMAL_BLOCK)我们发现operator new 和 operator delete本质上是对malloc和free的二次封装new和delete则上提供用户去调用operator new 和 operator delete的工具再抽象的说operator new和operator delete就像一个未装修的房子new和delete就是在此之上的装修师傅装修再怎么变房子还是那个房子。数组operator new[] 和 operator delete[] 同理5. new 和 delete 的实现原理内置类型如果申请的是内置类型的空间new和mallocdelete和free基本类似不同的地方是new/delete申请和释放的是单个元素的空间new[]和delete[]申请的是连续空间而且new在申请空间失败时会抛异常malloc会返回NULL。自定义类型new的原理调用 operator new 函数申请空间在申请的空间上执行构造函数完成对象的构造delete的原理在空间上执行析构函数完成对象中资源的清理工作调用operator delete函数释放对象的空间new[]/delete[] 和上述一致只不过调用的是operator new[] 和 operator delete[]6. 定位new表达式(placement-new)定位new 允许在 已分配的原始内存 上构造对象是将“内存分配”和“对象构造”它调用了一个特殊的、行为很简单的重载 operator newvoid*operatornew(size_t,void*mem)noexcept{returnmem;// 仅仅返回传入的指针不做任何分配}使用格式new (place_address) type或者new (place_address) type(initializer-list)place_address必须是一个指针initializer-list是类型的初始化列表classA{public:A(inta0):_a(a){coutA():thisendl;}~A(){cout~A()thisendl;}private:int_a;};intmain(){A*p1(A*)malloc(sizeof(A));new(p1)A;p1-~A();free(p1);p1nullptr;A*p2(A*)operatornew(sizeof(A));new(p2)A(10);p2-~A();operatordelete(p2);return0;}注意这里不要用new/delete因为如果不做定位new之前显示调用析构的话会未定义因为new/delete会自动调用构造/析构函数如果不在定位new之前去调用析构定位new就会再调用一下构造函数使用在工程中使用定位new优先使用operator new而非malloc。总结C/C内存管理上的区别C语言需要手动去赋值和释放C自动调用C语言失败返回NULLC抛异常C语言需要手动计算大小C由编译器根据类型自动计算返回void*需强制转换C返回严格类型指针更安全整个 C/C 内存管理就是在学怎么当个好地主。栈是随用随丢的草稿纸静态区是祖传田产堆是你需要动态买进卖出的田地。C 语言让你当赤脚地主一切自己跑腿C 让你当甩手东家有管家new/delete帮你打理但管家的内部活计operator new、构造/析构你要懂这样你才能雇更牛的管家重载甚至自己在老地基上直接起新楼placement new想怎么省就怎么省。完