必须用 free 释放 malloc 分配的内存、delete 释放 new 分配的单个对象、delete[] 释放 new[] 分配的数组;混用会因元信息不兼容导致未定义行为,引发崩溃、堆损坏或静默错误。

别用 free 释放 new 出来的内存,也别用 delete 释放 malloc 来的内存——混用会触发未定义行为(UB),程序可能当场崩溃、静默损坏堆、或在几天后某个毫无关联的函数里突然 segfault。
为什么 delete 和 free 不能互换?
根本原因是它们背后管理的“元信息”不兼容:delete 依赖编译器在 new 分配时悄悄写入的额外头信息(比如数组长度、类型描述符),用于调用析构函数;free 完全不认这些,只看 malloc 管理器自己维护的 chunk header。两者对同一块内存的“理解”完全不同。
常见错误现象:
- 用
free(p)释放new MyClass[10]分配的指针 → 程序直接 abort 或析构函数一个都不调 - 用
delete p释放malloc(100)的指针 → 可能 crash,也可能看似正常但破坏 malloc 内部链表 - 释放后继续访问(use-after-free)→
free后读写是未定义行为;delete后读写同理,且若对象有虚函数表,还可能跳转到非法地址
怎么一眼判断该用哪个?
看内存是谁分配的,不是看指针类型、不是看有没有析构函数、更不是看“感觉像不像对象”。就一条铁律:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用
malloc/calloc/realloc分配的 → 必须用free - 用
new(单个对象)分配的 → 必须用delete - 用
new[](数组)分配的 → 必须用delete[]
注意:new 和 new[] 不可互换,delete 和 delete[] 也不可互换。哪怕你 new int[1],也得用 delete[],否则编译器可能漏掉那个隐藏的数组长度字段读取,导致析构逻辑错乱(对 POD 类型可能侥幸存活,但对带析构函数的类必崩)。
实际项目中怎么防混用?
靠人盯代码不可靠,得靠机制:
- 禁用裸
malloc/free:在 C++ 项目里,把#include <cstdlib>加到禁止头文件列表,或用静态分析工具(如 clang-tidy 的cppcoreguidelines-no-malloc)拦截 - 用 RAII 封装:优先用
std::vector、std::unique_ptr、std::shared_ptr,它们内部已严格配对new/delete,你完全不用碰原始释放操作 - 重载全局
operator new/operator delete:在 debug build 中打日志,记录每次分配的调用栈和分配方式,运行时若检测到free试图释放new地址,立刻abort() - 启用 AddressSanitizer(ASan):它能在运行时捕获绝大多数
free/delete混用、重复释放、use-after-free,比靠经验排查快十倍
空指针释放安全吗?
安全,但仅限于标准规定的行为:
-
delete nullptr是明确定义的,什么也不做 -
delete[] nullptr同样合法 -
free(nullptr)也是标准允许的,无操作
所以无需写 if (p) delete p; 这种冗余检查。但要注意:这不意味着你可以放心传入野指针——delete 或 free 对非空但非法地址(比如已释放过的指针、栈地址、文字常量区地址)的行为全是未定义的,不会报错,只会埋雷。
最易被忽略的一点:C++ 标准只要求 delete 和 delete[] 能正确处理 nullptr,但没要求它们必须“识别出”你传进来的是不是 new 分配的地址。也就是说,即使你传了个看起来合法的非空指针,只要来源不匹配,结果仍是 UB——而这种错误在 ASan 关闭时往往悄无声息,直到上线后某次特定内存布局才爆发。

















