malloc 和 free 在 c++ 中能用但不该用,除非需 C 兼容、写底层封装或 no-std 环境;它们不调用构造/析构函数,应优先使用 std::vector、std::unique_ptr 等 RaiI 方式。

malloc 和 free 在 C++ 中能用,但不该用——除非你明确需要与 C 兼容、写底层封装、或在 no-std 环境中绕过 operator new。
malloc 和 free 的基本调用方式
它们是 C 标准库函数,头文件为 (C++ 中)或 (C 中)。malloc 只分配原始字节,不调用构造函数;free 只释放内存,不调用析构函数。
典型用法:
int* p = static_cast(malloc(sizeof(int) * 10)); if (p != nullptr) { for (int i = 0; i < 10; ++i) p[i] = i * 2; } // ... 使用后 free(p); // 注意:p 不会自动置为 nullptr p = nullptr; // 建议手动置空,避免悬垂指针
-
malloc返回void*,C++ 中必须显式static_cast(C 中可隐式转换,但不推荐) - 分配失败时返回
nullptr,**绝不能直接解引用** -
free(nullptr)是安全的,但free后重复调用会触发未定义行为 - 不能混用:
malloc+delete、new+free都是严重错误
为什么 malloc/free 在 C++ 中危险且易出错
核心问题在于:它完全绕过了 C++ 的对象生命周期管理机制。
立即学习“C语言免费学习笔记(深入)”;
- 对类类型使用
malloc:不会调用构造函数 → 对象处于未初始化状态 → 成员变量(尤其是指针、容器、智能指针)全是垃圾值 - 对含虚函数或非 POD 类型调用
free而不先显式调用析构函数:资源泄漏、vptr 损坏、后续访问崩溃 - 没有类型信息:无法做数组长度检查、无法重载、无法集成到 RAII 流程中
- 调试困难:ASan/UBSan 对
malloc/free的误用检测能力弱于new/delete
例如:
std::string* s = static_cast(malloc(sizeof(std::string))); // s 指向的内存里没有调用 std::string 构造函数! // 此时 *s 是未定义状态,s->c_str() 或赋值操作都会崩溃
什么情况下真得用 malloc/free
极少数合理场景,且通常出现在基础设施层:
- 实现自定义分配器(如
std::pmr::memory_resource子类),底层需要原始内存块 - 与 C ABI 交互:比如调用
dlopen/dlsym加载的函数,其文档明确要求用malloc分配传入缓冲区 - 嵌入式或 freestanding 环境(无
operator new实现),且标准库不可用 - 高性能内存池中管理大块页内存,再在其上 placement-new 构造对象(此时
malloc仅用于获取页,不直接存对象)
即便如此,也建议封装成 RAII 类(如 raw_memory_block),并在析构中调用 free,避免裸指针泄露。
替代方案:优先用 C++ 原生方式
99% 的日常开发应避开 malloc/free:
- 单个对象:用
new/delete(虽然也不推荐),更推荐std::make_unique/std::unique_ptr - 动态数组:用
std::vector,不是new T[n],更不是malloc(n * sizeof(T)) - 共享所有权:用
std::shared_ptr,配合std::make_shared - 需控制内存布局?用
std::allocator+std::vector::get_allocator(),或std::pmr::vector
例如,等价于上面的 int 数组,正确写法是:
auto v = std::vector(10); for (int i = 0; i < 10; ++i) v[i] = i * 2; // 作用域结束自动释放,无需手动 free
真正难处理的从来不是语法,而是忘记 malloc 给的是“字节”,不是“对象”;而 C++ 的强大,恰恰建立在把“对象”这件事管到底的基础上。