new与malloc区别

张开发
2026/4/14 16:46:10 15 分钟阅读

分享文章

new与malloc区别
前言动态分配内存我们经常用到的是new与malloc很多童鞋没有搞清楚这俩的本质区别导致了代码产生了一些非预期现象。今天跟大家一起来扒一扒它们的区别。目录一、new与malloc基本概述二、主要区别解析2.1 自由存储区 vs 堆2.2 指定类型指针 vs void* 指针2.3 构造函数与析构函数2.4 异常处理三、代码示例一、new与malloc基本概述new和malloc都是用于在运行时动态分配内存的机制但它们有着本质的区别。new是 C 语言中的关键字操作符专门用于在自由存储区free store上分配内存并构造对象。它不仅负责分配内存还会调用对象的构造函数来完成对象的初始化从而确保对象在使用前处于正确状态。delete是与之配套的释放操作会调用对象的析构函数并释放内存。这一整套机制是 C 提供的内存管理方案旨在通过自动调用构造/析构函数来简化对象的生命周期管理。相对而言malloc是 C 语言标准库中的库函数包含在cstdlib或stdlib.h中用于在堆heap上动态分配指定字节数的原始内存块。它仅仅分配内存不进行任何初始化操作。与之配套的free函数用于释放先前通过 malloc 分配的内存。malloc/free 是 C 语言进行动态内存管理的主要手段需要手动计算所需内存的大小并在不再需要时显式调用 free 来释放内存否则将导致内存泄漏。二、主要区别解析2.1 自由存储区 vs 堆new从自由存储区free store上为对象分配内存。自由存储区是 C 为了支持 new 操作而引入的一个抽象概念凡是使用 new 分配的内存都属于自由存储区。自由存储区在实现上可以基于堆但不一定等同于堆。例如可以通过重载operator new使得 new 从静态存储区甚至非堆内存区域分配对象。C 标准并未严格限定自由存储区的具体位置new 分配的内存位置取决于底层实现典型情况下是由 C 运行时在堆上分配。malloc直接从堆上分配内存。堆是操作系统维护的一块用于动态内存分配的内存区域属于计算机系统的底层概念。当调用 malloc 时它请求操作系统在堆中开辟一块指定大小的内存并返回该内存的起始地址。由于 malloc 直接使用堆它没有类似 C的内存管理机制。在实际应用中new 和 malloc 通常都会在进程的堆内存区域分配空间但从概念上讲new 分配的是自由存储区malloc 分配的是堆。2.2 指定类型指针 vs void* 指针new分配内存时编译器会根据请求的类型信息自动计算所需大小并返回指向该类型的指针无需进行类型转换。例如int* p new int;会分配一个 int 所需的内存并返回 int* 类型的指针。这种设计保证了类型安全——编译器会确保 new 返回的指针类型与请求的类型匹配保证了类型安全。malloc分配内存时需要显式指定所需内存的字节数并且 malloc 返回的是void*类型的通用指针。void* 可以指向任何类型的数据但本身不包含类型信息。因此使用 malloc 分配内存时通常需要将返回的 void* 指针强制转换为所需的指针类型。例如int* p (int*)malloc(sizeof(int));。这里sizeof(int)计算出 int 类型需要的字节数malloc 返回的 void* 被显式转换为 int*。这种类型转换增加了出错的机会如果类型不匹配可能引发未定义行为。此外由于 malloc 无法在编译时进行类型检查相比 new 缺乏类型安全性。2.3 构造函数与析构函数这是 new 与 malloc最核心的区别之一。new在分配内存的同时会自动调用对象的构造函数来初始化对象。当执行T* p new T(args);时编译器会完成两件事调用operator new底层通常基于 malloc分配足够大小的原始内存。在分配的内存上运行构造函数将对象初始化为指定状态。例如new A()会先调用operator new(sizeof(A))分配内存然后调用A::A()构造函数通过 new 分配的对象在使用前就已经完成了必要的初始化。delete在释放内存时会先自动调用对象的析构函数来清理对象占用的资源然后再释放内存。执行delete p;时会先调用p-~T()析构函数然后调用operator delete底层通常基于 free回收内存。new/delete 通过自动调用构造/析构函数实现了对对象生命周期的完整管理。与之相对malloc不调用任何构造函数或析构函数。malloc 分配的是原始的、未初始化的内存块其内容是未定义的。必须手动调用对象的构造函数来初始化否则对象将处于未初始化状态。同样地通过free释放内存时也不会调用任何析构函数必须手动调用这些析构逻辑否则会造成资源泄漏。简而言之malloc/free 只管内存的分配和释放不涉及对象的构造和析构增加了程序员的管理负担。2.4 异常处理当内存分配失败例如系统内存耗尽时new和malloc的处理方式也不同。new默认会抛出异常。C 标准规定如果 new 无法分配足够的内存它会抛出一个std::bad_alloc类型的异常。分配失败控制流会被异常机制立即中断。程序员可以通过 try-catch 块来捕获std::bad_alloc异常从而处理内存分配失败的情况。例如try { int* p new int[10000000]; // 尝试分配超大数组 } catch (const bad_alloc e) { cerr 内存分配失败: e.what() endl; // 进行错误处理例如释放资源、退出程序等 }malloc在分配失败时返回 NULL 指针。由于 malloc 是 C 语言风格的函数它没有异常机制因此通过返回值来指示错误。在使用 malloc 后必须检查返回值是否为 NULL以判断分配是否成功。如果忽略检查直接使用空指针将导致未定义行为程序崩溃。例如int* p (int*)malloc(sizeof(int) * 10000000); if (p NULL) { // 内存分配失败的处理 fprintf(stderr, 内存分配失败\n); exit(EXIT_FAILURE); } // 否则安全地使用 p...三、代码示例构造函数与析构函数的调用与否是new与malloc的最核心区别。这里我们通过代码示例展示一下#include iostream using namespace std; class A { public: A() : val(66) { cout A的构造函数被调用 endl; } ~A() { cout A的析构函数被调用 endl; } int val; }; int main() { // 使用 new 分配并初始化对象 cout --- 使用 new --- endl; A* a new A(); // 会调用 A 的构造函数 cout a-val a-val endl; delete a; // 会调用 A 的析构函数 // 使用 malloc 分配内存但不调用构造函数 cout endl --- 使用 malloc --- endl; A* aa (A*)malloc(sizeof(A)); // 只分配内存不调用构造函数 // 注意此时 aa-val 的值是未定义的垃圾值 // 手动调用构造函数来初始化对象 new(aa)A(); // 使用 placement new 在 aa 所指内存上调用构造函数 cout aa-val aa-val endl; // 手动调用析构函数清理对象然后释放内存 aa-~A(); free(aa); return 0; }可以看到使用 new 时构造函数和析构函数被自动调用而使用 malloc 时需要手动通过 placement new 调用构造函数并在 free 前手动调用析构函数。如果省略这些手动步骤malloc 分配的对象将不会被正确初始化或清理。写在最后在现代 C 中推荐优先使用智能指针如std::unique_ptr、std::shared_ptr来管理动态分配的对象而不是直接使用 new/delete。对于智能指针的解读感兴趣的童鞋可以看这篇文章C智能指针 解读_cpp智能指针-CSDN博客

更多文章