当前位置: 首页 > news >正文

C++——内存管理

内存分布介绍

C++程序运行时,内存被分为几个不同的区域,每个区域负责不同的任务。

  • 栈区(stack):由编译器自动分配释放。用来存放局部变量、函数参参、返回值等,还有函数调用也是栈来管理;
  • 堆区(heap):用于存储动态分配的内存的区域,由程序员手动分配和释放。主要相关函数有malloc()和free(),new和delete操作符;
  • 全局(静态)区:主要分为未初始化全局/静态区(.bass)和已初始化全局/静态区(.data)生命周期是整个程序运行期间。在程序启动时分配,程序结束时释放;
  • 常量区(.rodata):只读区,存储常量数据,如字符串常量等;
  • 代码区(.text):存放程序的代码;

堆与栈

说到C++的内存管理,那肯定要提到的两个就是堆和栈了,

是一种特殊的数据结构,遵循 “后进先出”(Last In First Out,LIFO)的原则,在程序执行过程中,堆栈主要用于存储临时数据,比如函数调用时的局部变量、函数参数和返回地址等。当一个函数被调用时,系统会在堆栈的顶部为其分配一块内存空间,用于存储该函数的局部变量和相关信息,这个过程称为 “压栈”(Push)。当函数执行结束后,这块内存空间会被自动释放,数据从堆栈中移除,这个过程称为 “出栈”(Pop)。由于堆栈的操作非常简单,只需要在栈顶进行压栈和出栈操作,所以它的速度非常快,

截然不同,是另一种用于存储数据的内存区域,它主要用于动态分配内存。与堆栈不同,堆中的数据存储顺序没有特定的规则,也不遵循 “后进先出” 的原则,更像是一个自由市场,你可以在任何时候根据需要申请或释放大小不同的内存块。在程序运行时,如果我们需要创建一个对象或者分配一块内存空间,就可以从堆中申请。例如,在 C++ 中,我们使用new关键字来从堆中分配内存;堆的优点是可以灵活地分配和释放内存,适合存储生命周期较长、大小不确定的数据。但是,由于堆的内存管理相对复杂,需要进行内存的分配和释放操作,所以它的速度相对较慢。

栈就像是一个高效的临时仓库,用于存储函数调用时的临时数据;而堆则像是一个自由市场,提供了更灵活的内存分配方式。它们在程序运行中都扮演着重要的角色,缺一不可。

栈的生命周期一般与函数的调用有关,当一个函数被调用时,系统会在堆栈的顶部为其分配一块内存空间,用于存储该函数的局部变量、函数参数和返回地址等信息,当函数执行结束后,它在堆栈中占用的内存空间会被自动释放。但是,由于堆栈的空间是有限的,如果在程序中不断地调用函数,或者在函数中定义大量的局部变量,就可能导致堆栈空间不足,从而引发栈溢出(Stack Overflow)错误

堆的生命周期则由程序员手动控制,在 C++ 中,我们使用new关键字来从堆中分配内存,使用delete关键字来释放内存;在使用堆内存时,程序员需要特别注意内存的分配和释放,避免内存泄漏和内存占用时间过长等问题。可以通过及时释放不再使用的内存,或者在程序设计中合理规划对象的生命周期,来提高内存的使用效率。

内存泄露

内存泄漏(memory leak)是指由于疏忽或错误造成了程序未能释放掉不再使⽤的内存的情况。内存泄漏并⾮指内存 在物理上的消失,⽽是应⽤程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因⽽造成了内存的浪费。

什么情况下会导致内存泄露呢?

  1. 比如指针指向改变,未释放动态分配内存。
  2. 程序运行中根据需要分配通过malloc、new等从堆中分配一块内存完成后必须通过调用对应的free或者delete删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被 使⽤
  3. 程序使⽤系统分配的资源没有使⽤相应的函数释放掉,导致系统资源的浪 费,严重可导致系统效能降低,系统运⾏不稳定。
  4. 当基类指针指向⼦类对象时,如果基类的析构函数不是 virtual,那么⼦类的析构函数将不会被调⽤,⼦类的资源没 有正确是释放,因此造成内存泄露。

如何对其进行预防?

  • 将内存的分配封装在类中,构造函数分配内存,析构函数释放内存;
  • 使用C++11新特性智能指针;

解决方法里也说了要封装在类中,构造函数分配、析构函数释放。但是构造函数和析构函数要设为虚函数吗?

析构函数是需要的,当派⽣类对象中有内存需要回收时,如果析构函数不是虚函数,不会触发动态绑定,只会调⽤基类 析构函数,导致派⽣类资源⽆法释放,造成内存泄漏。

构造函数不需要,没有意义。虚函数调⽤是在部分信息下完成⼯作的机制,允许我们只知道接⼝⽽不知道对象的确 切类型。 要创建⼀个对象,你需要知道对象的完整信息。 特别是,你需要知道你想要创建的确切类型。 因此,构 造函数不应该被定义为虚函数。

new、malloc、delete、free

  • new 是C++的运算符,可以为对象分配内存并调⽤相应的构造函数。
  • delete 会调⽤对象的析构函数,确保资源被正确释放。
  • malloc 是C语⾔库函数,只分配指定⼤⼩的内存块,不会调⽤构造函数。
  • free 不了解对象的构造和析构,只是简单地释放内存块。

返回类型:

  • new 返回的是具体类型的指针,⽽且不需要进⾏类型转换。
  • malloc 返回的是 void* ,需要进⾏类型转换,因为它不知道所分配内存的⽤途。

内存分配失败时的⾏为:

  • new 在内存分配失败时会抛出 std::bad_alloc 异常。
  • malloc 在内存分配失败时返回 NULL 。

内存块⼤⼩:

  • new 可以⽤于动态分配数组,并知道数组⼤⼩。
  • malloc 只是分配指定⼤⼩的内存块,不了解所分配内存块的具体⽤途。

内存块释放后的行为:

  • delete 释放的内存块的指针值会被设置为 nullptr ,以避免野指针。
  • free 不会修改指针的值,可能导致野指针问题。

野指针

为什么会产生野指针?

  • 释放后没有置空指针
  • 返回局部变量的指针
  • 释放内存后没有调整指针
  • 函数参数指针被释放

如何避免?

    • 在释放内存后将指针置为nullptr
    • 避免返回局部变量的指针
    • 使用智能指针

    野指针是指向已经被释放或者⽆效的内存地址的指针。通常由于指针指向的内存被释放,但指针本身没有被置为 nullptr 或者重新分配有效的内存,导致指针仍然包含之前的内存地址。

    悬浮指针是指向已经被销毁的对象的引⽤。当函数返回⼀个局部变量的引⽤,⽽调⽤者使⽤该引⽤时,就可能产⽣ 悬浮引⽤。访问悬浮引⽤会导致未定义⾏为,因为引⽤指向的对象已经被销毁,数据不再有效。

    内存对齐

    内存对⻬是指数据在内存中的存储起始地址是某个值的倍数。

    在结构体中,编译器为结构体的每个成员按 其⾃然边界(alignment)分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第⼀个成员的地址和整 个结构体的地址相同。

    ⽐如4字节的int 型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除,也即“对⻬”跟数据在内存中的位置有关。如果 ⼀个变量的内存地址正好位于它⻓度的整数倍,他就被称做⾃然对⻬。

    需要字节对⻬的根本原因在于CPU访问数据的效率问题。

    假设上⾯整型变量的地址不是⾃然对⻬,⽐如为 0x00000002,则CPU如果取它的值的话需要访问两次内存,第⼀次取从0x00000002-0x00000003的⼀个short, 第⼆次取从0x00000004-0x00000005的⼀个short然后组合得到所要的数据,如果变量在0x00000003地址上的话 则要访问三次内存,第⼀次为char,第⼆次为short,第三次为char,然后组合得到整型数据。 ⽽如果变量在⾃然对⻬位置上,则只要⼀次就可以取出数据。⼀些系统对对⻬要求⾮常严格

    http://www.dtcms.com/a/391834.html

    相关文章:

  • EasyClick JavaScript 错误处理机制
  • 六应用层-思考题
  • 多实例部署mysql
  • 信创国产化改造(改成人大金仓)
  • 远程修改的烦恼FacePoke和cpolar联手已帮您解决
  • 基于MCP的README生成协议
  • (1)什么是机器学习?
  • 【数据结构——邻接表】
  • RNA-seq分析之单基因Wilcoxon秩和检验
  • 四网络层IP-子网掩码ARP CIDR RIP OSPF BGP 路由算法-思考题
  • [重学Rust]之智能指针
  • 团体程序设计天梯赛-练习集 L1-036 A乘以B
  • H2数据库(tcp 服务器模式)调优
  • C# 面试记录
  • 深度学习(十):逻辑回归的代价函数
  • FreeRTOS学习笔记(六):汇编指令笔记
  • 【复刻】中国城市数字经济发展对环境污染的影响及机理研究(2011-2021年)
  • Blazer:一个免费开源、基于SQL的数据分析与可视化工具
  • 软件体系架构——系统架构评估与ATAM
  • sam2 docker部署
  • 深度学习------卷积神经网络
  • Amazon SES + NestJS 实战:零成本打造高送达率邮箱验证方案
  • MySQL 8.0临时表空间深度解析
  • 低秩矩阵:揭示高维数据中的简约之美
  • QR Wizard for Mac 好用的二维码生成器
  • 【redis】redis知识点
  • C语言模版(机试666)
  • 高通camx架构学习(二)——深入理解高通Camx Hal
  • 戴尔笔记本的奇怪功能
  • Linux文件系统结构与用户管理完全指南