C与C++(提高)动态内存分配——new(delete)与malloc(free)辨析—超详解

实验环境:(本次探讨所需要的平台和相关软件)

硬件环境:Windows 11

软件环境:Visual Studio 2022

 

程序的内存模型:

c++在执行程序时,将内存划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

内存四区意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程

 

堆区

  • 由程序员分配使用,若程序员不释放,程序结束时由操作系统回收
  • 在C++中主要利用 new在堆区动态开辟内存

//例1:

8bf788b0ec5048f381036cae4a1d5510.png

 

//结果:

e25e0a6eb92b4adba7380f9ccf4da910.png

 

 

//解释:

522ac4da2dcf49b1998d70b15febc8f3.png

 

 

  • 函数内部创建的指针 p,是在栈区开辟的指针变量,指针 p内存放了堆区中为存放10开辟的空间区域的地址编号
  • C++中可以利用关键字 new 开辟堆区空间
  • 堆区数据由程序员管理开辟和释放
  • 可以返回堆区的地址

为什么存在动态内存分配

 
0ed63048cbf34ebaaab32ae9e77cde31.png
但是上述的开辟空间的方式有两个特点:
 
1.空间开辟大小是固定的
2.数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配
 
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行时才能知道,那数组的编译时开辟空间的方式就不能满足了。这时候就只能试试动态开辟内存了。(在堆上分配)
  • 而我们动态创建变量(数组)时即是在堆区进行创建,我们知道,C++中通过new关键字动态开辟空间,使用delete释放;C语言中通过malloc关键字在动态开辟空间,使用free释放.它们之间有何区别,结合自己所学知识并参考了许多博主的文章,讨论C++与C中的动态内存分配区别,总结如下:

C++与C中的动态内存分配区别总结:

1. new与delete的使用:

(1)创建变量

  • new + 数据类型 (参数值),返回堆区开辟的地址,用一个相同数据类型的指针接收,如果开辟失败则抛出bac_alloc异常
  • 删除:关键字: delete

a4638ab693cf463aa5522618b94312a4.png

 

 

(2)利用 new创建数组

  • new  *** (数据类型)  [常量 n] ;表示在堆区开辟一个数组,数组有 n个元素,每个元素的类型是***,返回数组的首元素地址
  • 删除的时候: delete[ ] 数组名 

96d9c15ab22d49798c1937a716325e2b.png

 

2. malloc与free的使用:

C语言提供了一个动态内存开辟的函数

(1) malloc

原型:void * malloc  ( size_t  )
 
这个函数向内存申请一块连续可用的(size_t个字节的)空间,返回这块空间的起始地址
  • 如果开辟成功,则返回一个指向开辟好空间的指针
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查
  • 返回值的类型是 void* ,所以malloc 函数并不知道开辟空间的类型 , 具体在使用的时候由使用者自己来决定(强转)
  • 如果参数 size_t 为 0 ,则malloc 的行为是标准未定义的 ,取决于编译器
//示例:
动态内存开辟:
08b3b43567de44eda141e15facafca2c.png
这里需要整型指针来维护malloc返回的指针
 
C语言提供了另一个函数free,专门是用来做动态内存的释放和回收的

(2)free

函数原型:  void free ( void* memblock );
 
  • malloc与free是成对使用的,使用malloc向操作系统申请内存后,彻底不再使用时,要及时释放空间 free ,否则会出现内存泄漏
  • 如果参数 memblock 指向的空间不是动态开辟的,那么 free的行为是未定义的(释放的空间一定是由malloc 开辟出来的)
  • 如果 memblock 是NULL 指针 ,则函数什么事都不做
 
// 示例:
释放空间:
e3e4f82fd1b74b788b99090ff573fb02.png
 
malloc 和 free都声明在 stdlib.h 头文件中 。举个例子:
 
f1f152e69a89455a81af121d85dc6e87.png
 
解释:创建 含有num 个元素的整型数组

 

3. new与malloc区别:

均用于向动态申请一块连续的内存空间:

(1) 表格对比辨析

特征

new

malloc

属性

是运算符,存在于标准库函数中,可以重载,是关键字

是函数,引用时需要包含<malloc.h>头文件,不可以被重载,不是关键字

内存区域

①在自由存储区分配内存,由delete归还.

②特别的,new甚至可以不为对象分配内存,定位new的功能可以做到这一点:

3ac4751e8a1140bda69acf250c646c78.png

在堆区分配内存

内存泄漏对于new和malloc都能检测出来,new可以明确指出是哪个文件的哪一行,但是malloc不可以.

使用形式

①不需要指定空间大小,由系统自行推算;

②会调用指定数据类型的构造函数,可以对分匹配内存的进行初始化

①必须用sizeof运算符给出申请空间的大小;

②只管分配内存,不会调用数据类型的构造函数,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值是随机的.

处理数组

 

new 类型[]

(类型*)malloc(sizeof(类型)*数组大小)

 

 

返回值

成功

返回对象类型指针,与对象严格匹配,无类型转换,是符合类型安全性操作符

返回分配的内存区域地址void*,通常需要强制转换为指定类型指针才能使用.

失败

抛出异常bac_alloc,程序终止.

返回NULL.

效率

较高

较低

注意事项

①在使用动态开辟空间时,使用完成一定要释放空间,如果不释放会造内存泄漏。

②在使用动态开辟的空间中,不要进行指针的移动(如ptr++),因为一旦移动之后可能出现申请的空间和释放空间大小的不匹配问题

③当自定义的数据类型有在动态开辟的内存空间的变量时,必须自写深拷贝构造函数以及相应的析构函数及时释放.

     

(2) new或者malloc时经常会产生的一些问题:

接下来我们探讨在使用new或者malloc时经常会产生的一些问题:

 

a)链式结构中为什么不能对指针进行运算(ptr++或者ptr+1等)?

 

我们知道,通常我们在写链表的时候,对于单链表我们通过访问指针域的next进行下一内存地址的访问,如果是双链表我们通过指针域的front和next分别对前/后指向内存进行访问,那链式结构中为什么不能对指针进行运算(ptr++或者ptr+1等),实际上,链式结构是一个个结点通过指针域连接而成,实质上并不一定是一块连续的存储空间,ptr++或者ptr+1无法找到匹配这段空间大小的记录,所以并不能使用运算符,否则可能造成非法访问. ,此运算只能用于一块连续的存储空间.

 

b)为什么new(delete)的效率高于malloc(free)?

new和malloc都可用于申请动态内存和释放内存。对于非内部数据类型的对象(如自定义数据类型)而言,仅maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,在消亡之前要自动执行析构函数,malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free而new/delete可以。 C++中的new则能完成动态内存分配和初始化工作,以及delete能完成清理与释放内存工作,所以说new的效率高于malloc。

 

c)自由存储区和堆区?

①自由存储区(free store)是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区.

②堆(heap)C语言和操作系统的术语,堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,调用free()归还内存。一般情况下,所有的C++编译器默认使用堆来实现自由存储。

 

d)new可以分配两种内存:

new可以分配两种内存,单个变量的内存/连续的内存:

new只有分配单个内存的时候可以同时给该内存赋值(即调用对象的构造函数),分配连续多个内存时是无法同时赋值的

 

4.delete与free:

(1) 表格对比辨析

特征

delete

free

属性

运算符/关键字

函数/不是关键字

作用

释放new动态开辟的内存空间

释放malloc(或calloc等)动态开辟的内存空间

是否调用析构函数

调用

不调用

处理数组时

delete []arr

free(arr)

 

(2) 问题讨论:

a) 如果多次申请空间那么系统是如何做到空间的不重复使用呢?

这是因为,在使用动态内存开辟一段空间之后,系统会在这段空间之前做一个标记(0或1),当new或malloc开辟空间如果遇到标记为0就在此开辟空间,如果标记为1表示该块内存正在被占用,则不开辟.

相应的,当我们不再需要ptr指向的内存时,需要及时释放这块内存,将它归还给系统,否则该块内存一直处于被占用,其他变量无法对其使用,进而造成内存泄漏;当ptr指定的区域被释放以后,标记值会发生变化为0,表示变量ptr不再具有对这块内存的使用权,此时ptr可能指向随机一块内存,所以通常我们需要及时把ptr置为NULL,使其指向一块没有意义的空间,防止ptr非法访问某块内存区域.

(2) 释放时需要注意:

先判断该指针是否为NULL,防止重复释放.

 

(自身掌握知识有限,如有错误的地方或更好的建议,敬请指正!)

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值