content_views"
c lass="markdown_views prism-tomorrow-night">
cap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-bloc k" style="-webkit-tap-highlight-c olor: rgba(0, 0, 0, 0);"> c _0">new和malloc
前言
new和malloc 的知识点c ;作为一个嵌入式工程师是必须要了解清楚的。new和malloc 的区别到底在哪里呢
内存分配通常在以下场景下使用:
动态数据结构:如链表、栈、队列和图c ;这些数据结构的大小在程序运行时可能会变化。 大数据处理:当需要处理大块数据(如图像、文件数据等)时c ;动态分配可以根据实际需求分配内存。 用户输入:当用户输入的内容大小不可预见时c ;如读取不定长度的字符串。 资源管理:在需要创建大量对象c ;但具体数量在编译时无法确定的情况c ;如对象池或缓存系统。 动态内存分配提供了灵活性c ;使程序能够有效地管理内存c ;根据实际需求分配和释放内存。
一、属性上的区别
new/delete:是C++中的关键字(操作符)c ;若要使用c ;需要编译器支持; malloc /free:是标准库函数c ;若要使用则需要引入相应的头文件才可以正常使用。
二、使用上的区别
malloc :申请空间需要显式填入申请内存的大小; new:无需显式填入申请的内存大小c ;new会根据new的类型分配内存。
<c ode c lass="prism language-c ">class="token c omment">/** malloc /free **/
class="token keyword">int class="token operator">* a class="token operator">= class="token punc tuation">( class="token keyword">int class="token operator">* class="token punc tuation">) class="token func tion">malloc class="token punc tuation">( class="token number">4 class="token punc tuation">) ;
class="token func tion">free class="token punc tuation">( aclass="token punc tuation">) ;
c ode>
在这里c ;malloc (4) 分配了 4 字节的内存。由于 int 类型通常占用 4 字节c ;因此分配了足够存储一个 int 类型的数据。free 函数用于释放之前通过 malloc 或其他动态内存分配函数分配的内存。在这里c ;free(a) 释放了指针 a 指向的 4 字节的内存块。
<c ode c lass="prism language-c ">class="token mac ro property">class="token direc tive-hash"># class="token direc tive keyword">inc lude class="token string"><stdio.h>
class="token mac ro property">class="token direc tive-hash"># class="token direc tive keyword">inc lude class="token string"><stdlib.h> class="token keyword">int class="token func tion">main class="token punc tuation">( class="token punc tuation">) class="token punc tuation">{ class="token c omment">// 动态分配内存以存储一个整数 class="token keyword">int class="token operator">* a class="token operator">= class="token punc tuation">( class="token keyword">int class="token operator">* class="token punc tuation">) class="token func tion">malloc class="token punc tuation">( class="token keyword">sizeof class="token punc tuation">( class="token keyword">int class="token punc tuation">) class="token punc tuation">) class="token punc tuation">; class="token c omment">// 检查内存分配是否成功 class="token keyword">if class="token punc tuation">( a class="token operator">== class="token c onstant">NULL class="token punc tuation">) class="token punc tuation">{ class="token func tion">printf class="token punc tuation">( class="token string">"内存分配失败\n" class="token punc tuation">) class="token punc tuation">; class="token keyword">return class="token number">1 class="token punc tuation">; class="token c omment">// 结束程序并返回错误代码 class="token punc tuation">} class="token c omment">// 设置分配的内存中的值 class="token operator">* a class="token operator">= class="token number">42 class="token punc tuation">; class="token c omment">// 打印内存中的值 class="token func tion">printf class="token punc tuation">( class="token string">"内存中的值是: %d\n" class="token punc tuation">, class="token operator">* aclass="token punc tuation">) class="token punc tuation">; class="token c omment">// 释放分配的内存 class="token func tion">free class="token punc tuation">( aclass="token punc tuation">) class="token punc tuation">; class="token c omment">// 将指针设为 NULLc ;避免悬挂指针 a class="token operator">= class="token c onstant">NULL class="token punc tuation">; class="token keyword">return class="token number">0 class="token punc tuation">;
class="token punc tuation">} c ode>
c="https://i-blog.c sdnimg.c n/direc t/88be240343b44bbd89530b34506262dc .png" alt="在这里插入图片描述" /> 在调用 free 函数之前c ;确保指针确实指向了动态分配的内存是非常重要的。 为什么需要确保指针指向动态分配的内存? 1.内存释放的正确性: free 函数的作用是释放之前由 malloc 、c alloc 或 realloc 函数分配的内存。如果指针 a 不指向有效的动态分配内存区域(即没有通过这些函数分配的内存)c ;调用 free(a) 可能会导致未定义行为。未定义行为可能会导致程序崩溃、内存泄漏、数据损坏或其他难以预测的错误。 2.内存管理的安全性: 如果 a 指向非动态分配的内存(比如一个局部变量、全局变量或者静态变量)c ;调用 free 可能会导致操作系统试图释放不属于它的内存c ;造成错误。例如c ;如果你试图释放一个未分配的指针或者已经被释放的指针(悬挂指针)c ;这也会导致问题。 确保指针指向动态内存的措施 1.初始化指针: 在使用指针之前c ;初始化它为 NULL。这样c ;如果你忘记分配内存c ;它至少不会指向一个不确定的位置。 例如:int *a = NULL; 分配内存之后检查指针: 每次调用 malloc 或相关函数后c ;都应该检查指针是否为 NULL。如果为 NULLc ;说明内存分配失2.败c ;需要处理这种情况。 2.避免重复释放 : 确保每个动态分配的内存块只被释放一次。重复释放同一块内存会导致未定义行为。 在释放内存后c ;可以将指针设置为 NULLc ;这有助于避免对已经释放内存的重复释放尝试。
<c ode c lass="prism language-c ">class="token func tion">free class="token punc tuation">( aclass="token punc tuation">) class="token punc tuation">;
a class="token operator">= class="token c onstant">NULL class="token punc tuation">;
c ode>
3.管理指针的生命周期: 确保在释放内存之前c ;所有指针操作都合法且在范围内。避免在释放内存后还尝试使用该内存(如访问已释放的内存)。
<c ode c lass="prism language-c "> class="token c omment">// 尝试释放已经释放的指针c ;安全地检查 class="token keyword">if class="token punc tuation">( a class="token operator">!= class="token c onstant">NULL class="token punc tuation">) class="token punc tuation">{ class="token func tion">free class="token punc tuation">( aclass="token punc tuation">) class="token punc tuation">; class="token c omment">// 这行不会被执行c ;因为 ma 是 NULL class="token punc tuation">}
c ode>
<c ode c lass="prism language-c ">class="token c omment">/** new/delete **/
class="token keyword">int class="token operator">* b class="token operator">= new class="token keyword">int class="token punc tuation">( class="token number">0 class="token punc tuation">) class="token punc tuation">;
c ode>
分配内存:使用 new 关键字在堆上分配内存来存储一个 int 类型的值。 初始化内存:将这个 int 初始化为 0。 返回指针:new 操作符返回一个指向这块内存的指针c ;该指针被赋值给 b。 因此c ;b 是一个指向 int 类型的指针c ;指向的内存位置存储着值 0。
<c ode c lass="prism language-c ">class="token mac ro property">class="token direc tive-hash"># class="token direc tive keyword">inc lude class="token string"><iostream> class="token keyword">int class="token func tion">main class="token punc tuation">( class="token punc tuation">) class="token punc tuation">{ class="token c omment">// 使用 new 操作符分配内存并初始化为 0 class="token keyword">int class="token operator">* b class="token operator">= new class="token keyword">int class="token punc tuation">( class="token number">0 class="token punc tuation">) class="token punc tuation">; class="token c omment">// 输出指针 b 指向的值 stdclass="token operator">:: c out class="token operator"><< class="token string">"The value of *b is: " class="token operator"><< class="token operator">* b class="token operator"><< stdclass="token operator">:: endlclass="token punc tuation">; class="token c omment">// 修改 b 指向的值 class="token operator">* b class="token operator">= class="token number">42 class="token punc tuation">; stdclass="token operator">:: c out class="token operator"><< class="token string">"The new value of *b is: " class="token operator"><< class="token operator">* b class="token operator"><< stdclass="token operator">:: endlclass="token punc tuation">; class="token c omment">// 释放分配的内存 避免内存泄漏 delete bclass="token punc tuation">; class="token c omment">// 将 b 设置为 nullptrc ;以避免悬挂指针 b class="token operator">= nullptrclass="token punc tuation">; class="token keyword">return class="token number">0 class="token punc tuation">;
class="token punc tuation">} c ode>
c="https://i-blog.c sdnimg.c n/direc t/a79373b720e4455a9dc 41a6719925096.png" alt="在这里插入图片描述" />
三、内存位置的区别
new:此操作符分配的内存空间是在自由存储区; malloc :申请的内存是在堆空间。 C/C++的内存通常分为:堆、栈、自由存储区、全局/静态存储区、常量存储区。可能除了自由存储区c ;其他的内存分布大家应该都比较熟悉。堆:是C语言和操作系统的术语c ;堆是操作系统所维护的一块特殊内存c ;它提供了动态分配的功能c ;当运行程序调用malloc ()时就会从中分配c ;调用free()归还内存。自由存储区:是C++中动态分配和释放对象的一个概念c ;通过new分配的内存区域可以称为自由存储区c ;通过delete释放归还内存。自由存储区可以是堆、全局/静态存储区等c ;具体是在哪个区c ;主要还是要看new的实现以及C++编译器默认new申请的内存是在哪里。但是基本上c ;很多C++编译器默认使用堆来实现自由存储c ;运算符new和delete内部默认是使用malloc 和free的方式来被实现c ;说它在堆上也对c ;说它在自由存储区上也正确。因为在C++中new和delete符号是可以重载的c ;我们可以重新实现new的实现代码c ;可以让其分配的内存位置在静态存储区等。而malloc 和free是C里的库函数c ;无法对其进行重载。
四、返回类型的区别
new操作符内存分配成功时c ;返回的是对象类型的指针c ;类型严格与对象匹配c ;无须进行类型转换c ;故new是符合类型安全性的操作符。 malloc 内存分配成功则是返回void* c ;需要通过强制类型转换将void*指针转换成我们需要的类型。所以在C++程序中使用new会比malloc 安全可靠。
五、分配失败的区别
malloc 分配内存失败时返回NULLc ;我们可以通过判断返回值可以得知是否分配成功; new内存分配失败时c ;会抛出bac _alloc 异常c ;它不会返回NULLc ;分配失败时如果不捕捉异常c ;那么程序就会异常退出。
六、扩张内存的区别
malloc :使用malloc 分配内存后c ;发现内存不够用c ;那我们可以通过realloc 函数来扩张内存大小c ;realloc 会先判断当前申请的内存后面是否还有足够的内存空间进行扩张c ;如果有足够的空间c ;那么就会往后面继续申请空间c ;并返回原来的地址指针;否则realloc 会在另外有足够大小的内存申请一块空间c ;并将当前内存空间里的内容拷贝到新的内存空间里c ;最后返回新的地址指针。 new:new没有扩张内存的机制。
七、系统调度过程的区别
malloc free是库函数而不是运算符c ;不在编译器控制范围之内c ;不能够自动调用构造函数和析构函数。NEW在为对象申请分配内存空间时c ;可以自动调用构造函数c ;同时也可以完成对对象的初始化。同理c ;delete也可以自动调用析构函数。而malloc 只是做一件事c ;只是为变量分配了内存c ;同理c ;free也只是释放变量的内存。
总结
new:
<c ode c lass="prism language-c ">class="token keyword">int class="token operator">* p class="token operator">= new class="token keyword">int class="token punc tuation">( class="token number">5 class="token punc tuation">) class="token punc tuation">; class="token c omment">// 分配内存并初始化为 5
c ode>
在 C++ 中使用。 会调用构造函数来初始化对象。 用于分配单个对象或数组。 malloc :
<c ode c lass="prism language-c ">class="token keyword">int class="token operator">* p class="token operator">= class="token punc tuation">( class="token keyword">int class="token operator">* class="token punc tuation">) class="token func tion">malloc class="token punc tuation">( class="token keyword">sizeof class="token punc tuation">( class="token keyword">int class="token punc tuation">) class="token punc tuation">) class="token punc tuation">; class="token c omment">// 分配内存c ;但不初始化
c ode>
在 C 和 C++ 中使用。 不会调用构造函数c ;内存中的内容是未定义的。 需要强制转换为目标类型。
内存释放 new:使用 delete 或 delete[] 来释放内存。
<c ode c lass="prism language-c ">delete pclass="token punc tuation">; class="token c omment">// 对应单个对象
deleteclass="token punc tuation">[ class="token punc tuation">] pclass="token punc tuation">; class="token c omment">// 对应对象数组
c ode>
malloc :使用 free 来释放内存。
<c ode c lass="prism language-c ">class="token func tion">free class="token punc tuation">( pclass="token punc tuation">) class="token punc tuation">;
c ode>
异常处理 new: 如果内存分配失败c ;new 会抛出 std::bad_alloc 异常。 可以使用 new(std::nothrow) 来避免抛出异常c ;而是返回 nullptr。 malloc : 如果内存分配失败c ;malloc 返回 NULL。不会抛出异常c ;适用于不支持 C++ 异常的环境。 适用场景 new: 适用于 C++ 编程c ;特别是当你需要初始化对象或管理对象生命周期时。 与 C++ 的对象构造和析构机制兼容。 malloc : 适用于 C 或者 C++ 中的低级内存管理c ;尤其是当你在 C++ 环境中需要兼容 C 代码时。 更适合需要原始内存块的场景c ;但需要手动处理初始化和类型转换。