C++动态内存管理:与C语言动态内存管理的差异之争

2024-05-11 04:12

本文主要是介绍C++动态内存管理:与C语言动态内存管理的差异之争,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

当你改错一行代码的时候:

当你想要重构别人的代码时:

目录

前言

一、C/C++的内存分布

二、C/C++语言中的动态内存管理

 三、new与delete的实现原理

总结:

 


前言

在C++中,内存管理是一个至关重要的主题。正确地管理内存可以避免内存泄漏和内存访问错误等问题,提高程序的性能和可靠性。本文将深入探讨C++中的内存管理机制,包括从C语言内存管理到C++内存管理转变,二者的关联等内容。

一、C/C++的内存分布

C/C++程序内存主要使用了以下几个区域,让我们先来认识一下:

1、内核区域:

“内核区域”通常指的是操作系统的核心部分,它是操作系统的基本组成部分之一。内核负责管理计算机的硬件资源,并提供给应用程序访问这些资源的接口。但是我们用户的代码读写不了它,所以这里也不主要讲解。

2、栈区:

栈区是计算机内存中的一部分,用于存储函数调用时的局部变量、函数参数、函数返回地址等临时数据。栈是一种后进先出(LIFO)的数据结构,因此栈区也被称为后进先出(LIFO)内存区域。

在典型的程序执行过程中,每当一个函数被调用时,该函数的局部变量和参数被分配到栈区,并且该函数的返回地址被推入栈中。当函数执行完成后,栈中的这些数据被销毁,栈的指针回退到上一个函数的栈帧。这种方式使得程序能够有效地管理函数调用和返回。

栈区的大小通常是固定的,由操作系统在程序启动时分配。如果栈区的空间被耗尽,就会发生栈溢出(stack overflow)错误,这通常是由于递归函数调用层数过多或者局部变量占用过多栈空间所导致的。

3、内存映射段:

存映射段(Memory-mapped segment)是指操作系统中的一种机制,它允许将文件或其他设备映射到进程的地址空间,使得这些文件或设备能够像内存一样被访问和操作。(我们这里也不讲解,只是单纯提一下)

4、堆区:

堆区是计算机内存中的一部分,用于动态分配内存空间。在堆区中,程序员可以通过调用诸如 malloc()、calloc() 或 new 等函数来请求内存空间,用于存储程序中动态创建的数据结构,比如链表、树、对象等。(也就是我们今天所要讲解的动态内存管理所涉及的区域)

5、数据段:

数据段(Data Segment)是指存储程序中已声明的全局变量和静态变量的内存区域。数据段通常位于进程的虚拟地址空间中的一个固定位置,并且在程序加载时被分配。(全局变量与static修饰的变量)

(静态变量主要有三种方式:

1、在函数内部使用 static 关键字声明

2、在全局作用域内使用 static 关键字声明

3、在函数内部不使用 static 关键字,但函数外部使用 static 修饰该函数

6、代码段:

代码段中通常存储着可执行的程序与只读常量如我们规定的“a,n,f,j”等字符

二、C/C++语言中的动态内存管理

1、C语言:

在C语言中,我们通常使用malloc/calloc/realloc/free四个关键字来管理我们的动态内存分配。

malloc()函数:

  • void *malloc(size_t size);
  • 用于动态分配指定大小的内存块。
  • 返回一个指向分配的内存块的指针,或者在分配失败时返回 NULL。

calloc()函数 :

  • void *calloc(size_t num, size_t size);
  • 用于动态分配指定数量和大小的内存块,并将其初始化为零。
  • 返回一个指向分配的内存块的指针,或者在分配失败时返回 NULL。

realloc()函数;

  • void *realloc(void *ptr, size_t size);
  • 用于更改之前分配的内存块的大小。
  • 返回一个指向重新分配的内存块的指针,或者在分配失败时返回 NULL。

 free()函数:

  • void free(void *ptr);
  • 用于释放之前分配的内存块。

 在C语言中,malloc()等函数会返回 void* 类型的指针,需要强制类型转换为目标类型,这使得使用十分不方便。而C++内存管理只需要用new与delete两个运算符就可以做得更加高级,更加安全便捷。

2、C++:
C++中用 newdelete 运算符来动态地分配和释放内存。

new运算符:

  • new 运算符用于动态分配内存空间,并返回所分配内存的地址。
  • new 运算符的基本语法为:new 数据类型new 数据类型[数组大小]
  • 动态分配的内存必须由程序员显式地释放,否则会造成内存泄漏。

delete运算符:

  • delete 运算符用于释放动态分配的内存空间。
  • delete 运算符的基本语法为:delete 指针变量delete[] 指针变量(释放数组)。
  • 动态分配的内存在不再使用时必须由程序员显式地释放,否则会造成内存泄漏。

 (其实在C++11中还新增了智能指针的概念,但现在我们先不提及)

int main()
{int* arr = new int[10];// 分配一个包含10个整数的内存块int* ptr = new int; // 动态分配一个整数的内存空间int* ptr2 = new int(10); // 动态分配一个整数的内存空间并且初始化为10delete ptr;delete ptr2;//释放内存delete[] arr;//加上[]表示多个数据return 0;
}

//分配自定义类型class A
{
public:A(int a = 0):_a(a){cout << "A():" << endl;}~A(){cout << "~A():" << endl;}
private:int _a;};int main()
{A* ptr1 = new A;A* ptr2 = (A*)malloc(sizeof(A));delete ptr1;free(ptr2);return 0;
}
结果可以发现,我们在new与delete的时候会自动调用自定义类型的构造与析构函数,而C的malloc与free则不会。
new delete 是用户进行 动态内存申请和释放的操作符 operator new operator delete
系统提供的 全局函数 new 在底层调用 operator new 全局函数来申请空间, delete 在底层通过
operator delete 全局函数来释放空间。

实际上,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。(以后讲解异常概念)operator delete 最终是通过free来释放空间的

 三、new与delete的实现原理

1、对于内置类型来说:

new与malloc,delete与free基本类似,不同的地方是:

new与delete申请与释放的是单个元素的空间,new[]与delete[]申请与释放的是连续的空间,而且new在申请失败时会抛出异常,malloc则会直接返回NULL。

2、对于自定义类型来说:

new的原理:

1. 调用 operator new 函数申请空间
2. 在申请的空间上执行构造函数,完成对象的构造
delete的原理
 
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用 operator delete 函数释放对象的空间
new T[N] 的原理:
1. 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对
象空间的申请
2. 在申请的空间上执行 N 次构造函数
delete[] 的原理
1. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理
2. 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释
放空间

总结:

malloc/free new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地
方是:
1. mallocfree是函数,newdelete是操作符
2. malloc申请的空间不会初始化,new可以初始化
3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,
如果是多个对象,[]中指定对象个数即可
4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new
要捕获异常
6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new
在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成
空间中资源的清理

 

希望通过本篇文章,能够对你区分C++与C语言内存管理有所帮助。

这篇关于C++动态内存管理:与C语言动态内存管理的差异之争的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978468

相关文章

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

关于C++中的虚拟继承的一些总结(虚拟继承,覆盖,派生,隐藏)

1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下: class A class B1:public virtual A; class B2:pu

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

C++的模板(八):子系统

平常所见的大部分模板代码,模板所传的参数类型,到了模板里面,或实例化为对象,或嵌入模板内部结构中,或在模板内又派生了子类。不管怎样,最终他们在模板内,直接或间接,都实例化成对象了。 但这不是唯一的用法。试想一下。如果在模板内限制调用参数类型的构造函数会发生什么?参数类的对象在模板内无法构造。他们只能从模板的成员函数传入。模板不保存这些对象或者只保存他们的指针。因为构造函数被分离,这些指针在模板外

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

C/C++的编译和链接过程

目录 从源文件生成可执行文件(书中第2章) 1.Preprocessing预处理——预处理器cpp 2.Compilation编译——编译器cll ps:vs中优化选项设置 3.Assembly汇编——汇编器as ps:vs中汇编输出文件设置 4.Linking链接——链接器ld 符号 模块,库 链接过程——链接器 链接过程 1.简单链接的例子 2.链接过程 3.地址和

用Microsoft.Extensions.Hosting 管理WPF项目.

首先引入必要的包: <ItemGroup><PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /><PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /><PackageReference Include="Serilog

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C++学习 贝蒂的主页:Betty’s blog 1. 泛型编程 首先让我们来思考一个问题,如何实现一个交换函数? void swap(int& x, int& y){int tmp = x;x = y;y = tmp;} 相信大家很快就能写出上面这段代码,但是如果要求这个交换函数支持字符型

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

关于如何更好管理好数据库的一点思考

本文尝试从数据库设计理论、ER图简介、性能优化、避免过度设计及权限管理方面进行思考阐述。 一、数据库范式 以下通过详细的示例说明数据库范式的概念,将逐步规范化一个例子,逐级说明每个范式的要求和变换过程。 示例:学生课程登记系统 初始表格如下: 学生ID学生姓名课程ID课程名称教师教师办公室1张三101数学王老师101室2李四102英语李老师102室3王五101数学王老师101室4赵六103物理陈