ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C++内存管理:从内存分布到new/delete底层原理

2026/8/10 10:03:16 拓冰建站 浏览量
C++内存管理:从内存分布到new/delete底层原理

一、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); }

你能准确说出每个变量存储在哪个区域吗?

  • globalvar→ 数据段(静态区)

  • staticGlobalvar→ 数据段(静态区)

  • staticvar→ 数据段(静态区)

  • localvar→ 栈

  • num1→ 栈

  • char2→ 栈

  • *char2→ 栈(因为char2是数组,内容在栈上)

  • pChar3→ 栈

  • *pChar3→ 代码段(常量区),因为指向的是字符串常量

  • ptr1→ 栈

  • *ptr1→ 堆

内存区域划分

区域存储内容特点
非静态局部变量、函数参数、返回值等向下增长,自动管理
动态分配的内存向上增长,需手动管理
数据段(静态区)全局变量、静态变量程序运行时分配,结束时释放
代码段(常量区)可执行代码、只读常量只读,不可修改


二、C语言的动态内存管理

在C语言中,我们使用malloccallocreallocfree来管理动态内存。

malloc/calloc/realloc 的区别

函数参数初始化返回值
malloc字节数不初始化void*
calloc个数,每个大小初始化为0void*
realloc原指针,新大小不初始化void*
// 使用示例 int* p2 = (int*)malloc(4 * sizeof(int)); // 申请4个int int* p3 = (int*)calloc(4, sizeof(int)); // 申请4个int并初始化为0 int* p4 = (int*)realloc(p2, sizeof(int)*10); // 扩容到10个int // 注意:如果realloc扩容成功,p2会被自动释放,不需要再free(p2) free(p3); free(p4);

三、C++的内存管理方式

C++除了兼容C的内存管理方式外,还引入了newdelete操作符。

3.1 内置类型的使用

// 申请和释放单个对象 int* ptr1 = new int; // 申请一个int int* ptr2 = new int(10); // 申请一个int并初始化为10 delete ptr1; delete ptr2; // 申请和释放数组 int* ptr3 = new int[10]; // 申请10个int delete[] ptr3; // 注意:必须使用delete[]

3.2 自定义类型的使用

newdeletemallocfree最大的区别: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() { // malloc只会开辟空间,不会调用构造函数 A* p1 = (A*)malloc(sizeof(A)); // new会开辟空间 + 调用构造函数 A* p2 = new A(10); // free只会释放空间,不会调用析构函数 free(p1); // delete会调用析构函数 + 释放空间 delete p2; return 0; }

四、operator new 与 operator delete

很多人不知道的是,newdelete实际上是操作符,而operator newoperator delete全局函数

底层实现原理

// operator new 的简化实现 void* __CRTDECL operator new(size_t size) { void* p; while ((p = malloc(size)) == 0) { if (_callnewh(size) == 0) { // 申请失败,抛出 bad_alloc 异常 throw std::bad_alloc(); } } return p; } // operator delete 的简化实现 void operator delete(void* pUserData) { if (pUserData == NULL) return; free(pUserData); }

关键点

  • operator new底层调用malloc,申请失败时抛异常而不是返回NULL

  • operator delete底层调用free

五、new 和 delete 的实现原理

5.1 内置类型

对于内置类型,newmallocdeletefree基本类似,区别在于:

  • new失败时抛异常,malloc失败返回NULL

  • new[]/delete[]用于连续空间

5.2 自定义类型

new的原理

  1. 调用operator new申请空间

  2. 在申请的空间上执行构造函数

delete的原理

  1. 在空间上执行析构函数

  2. 调用operator delete释放空间

new T[N]的原理

  1. 调用operator new[](实际调用operator new)申请N个对象空间

  2. 执行N次构造函数

delete[]的原理

  1. 执行N次析构函数

  2. 调用operator delete[](实际调用operator delete)释放空间

六、定位new表达式(placement new)

定位new允许在已分配的内存上调用构造函数初始化对象。

// 使用场景:内存池分配的内存需要初始化 A* p1 = (A*)malloc(sizeof(A)); // 只分配内存,未构造对象 new(p1) A(10); // 在p1指向的内存上构造对象 p1->~A(); // 显式调用析构函数 free(p1);

适用场景:配合内存池使用,在已分配的内存上创建对象。

七、malloc/free 与 new/delete 的区别

对比项malloc/freenew/delete
本质函数操作符
初始化不会初始化可以初始化
大小计算需手动计算编译器自动计算
返回值void*,需强转类型安全,无需强转
失败处理返回NULL,需判空抛异常,需捕获
自定义类型不调用构造/析构调用构造/析构

八、常见问题与最佳实践

8.1 内存泄漏

内存泄漏是指动态分配的内存没有被正确释放,导致内存浪费。

常见场景

  • 忘记delete/free

  • 异常导致delete未执行

  • 使用new[]却用delete释放

8.2 最佳实践

  1. 匹配使用newdeletenew[]delete[]

  2. RAII原则:使用智能指针(unique_ptrshared_ptr)管理资源

  3. 避免裸指针:尽可能使用智能指针

  4. 定期检查:使用内存检测工具定期检查内存泄漏

总结

C++的内存管理非常灵活但也很容易出错。记住以下几点:

  1. 理解内存分布(栈、堆、数据段、代码段)

  2. 掌握new/deletemalloc/free的区别

  3. 理解operator new/operator delete的底层原理

  4. 遵循最佳实践,使用智能指针管理内存