【C++语言】C/C++内存管理

news2025/1/9 1:25:16

一、C/C++内存分布

       我们先来看一看C/C++中有哪些区域,为什么C/C++中区分这些区域呢??不同的数据有不同的存储需求,各个区域满足不同的需求。我们有临时用的数据,该数据是存储在栈帧区域的;在一些数据结构中,算法中需要动态开辟一些空间,比如:归并排序,这些数据是存储在堆区的;在整个程序期间都要使用的数据,我们称之为只读数据,像全局数据、静态数据存储在静态区中,像可执行代码和只读常量存储在常量区中。

我们下面来总结一下:

  1. 栈又叫做堆栈——非静态局部变量/函数参数/返回值等等,栈是向下增长的
  2. 内存映射段是高效的I/O映射方式,用于装载到一个共享的动态内存库,用户可以使用系统接口创建共享内存,进行进程间通信
  3. 堆用于程序运行时动态内存分配,堆是可以向上增长的
  4. 数据段(静态区)——存储全局数据和静态数据
  5. 代码段(常量区)——可执行的代码和只读常量

1.2 练习习题

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
    static int staticVar = 1;
    int localVar = 1;
    int num1[10] = {1, 2, 3, 4};
    char char2[] = "abcd";
    const char *pChar3 = "abcd";
    int *ptr1 = (int *)malloc(sizeof(int) * 4);
    int *ptr2 = (int *)calloc(4, sizeof(int));
    int *ptr3 = (int *)realloc(ptr2, sizeof(int) * 4);
    free(ptr1);
    free(ptr3);
}

选择题:

选项:A.栈     B.堆    C.数据段(静态区)  D.代码段(常量区)

  • globalVar在哪里?  全局变量,在静态区  C
  • staticGlobalVar在哪里? 全局变量,用static修改,静态变量,在静态区 C
  • staticVar在哪里?  用static修饰,静态变量,但是作用范围被限制在函数中  在静态区 C
  • localVar在哪里?  局部变量,在栈区  A
  • num1 在哪里?  局部变量,在栈区  A
  • char2在哪里?  局部变量,在栈区开辟数组,在栈区  A
  • *char2在哪里?  局部变量,指针指向的地方在栈区 A
  • pChar3在哪里?  数组指针,局部变量,在栈区 A
  • *pChar3在哪里?  字面值常量,只读常量,在常量区  D
  • ptr1在哪里?  指针,局部变量,在栈区  A
  • *ptr1在哪里?  利用malloc函数开辟空间,在堆区  B

填空题:

  • sizeof(num1) =  40
  • sizeof(char2) =  5
  • strlen(char2) =  4
  • sizeof(pChar3) =  4/8
  • strlen(pChar3) =  4
  • sizeof(ptr1) =  4/8

二、C++内存管理方式

       C语言内存管理方式在C++中可以继续使用,但是在有些地方就无能为力了,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符来进行动态内存管理

2.1 new/delete操作内置类型

void Test()
{
    // 动态申请一个int类型的空间
    int *ptr4 = new int;
    // 动态申请一个int类型的空间并初始化为10
    int *ptr5 = new int(10);
    // 动态申请10个int类型的空间
    int *ptr6 = new int[10];
    delete ptr4;
    delete ptr5;
    delete[] ptr6;
}

 

       注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意,要进行匹配使用

2.2 new/delete操作自定义类型(围绕)

class A
{
public:
    A(int a = 0)
        : _a(a)
    {
        cout << "A():" << this << endl;
    }
    ~A()
    {
        cout << "~A():" << this << endl;
    }

private:
    int _a;
};
int main()
{
    // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构
    造函数和析构函数
    A *p1 = (A *)malloc(sizeof(A));
    A *p2 = new A(1);
    free(p1);
    delete p2;
    // 内置类型是几乎是一样的
    int *p3 = (int *)malloc(sizeof(int)); // C
    int *p4 = new int;
    free(p3);
    delete p4;
    A *p5 = (A *)malloc(sizeof(A) * 10);
    A *p6 = new A[10];
    free(p5);
    delete[] p6;
    return 0;
}

       注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,但是malloc和free不会。

三、operator new 和 operator delete 函数

       new和delete函数是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层调用operator delete全局函数来释放空间。

       我们来看一下这两个函数在底层是如何封装的?我们在C语言中使用malloc函数和free函数来封装operator new 和 operator delete函数,接下来,我们来看一看底层的代码:

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
    // try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
        if (_callnewh(size) == 0)
        {
            // report no memory
            // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
            static const std::bad_alloc nomem;
            _RAISE(nomem);
        }
    return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
    _CrtMemBlockHeader *pHead;
    RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
    if (pUserData == NULL)
        return;
    _mlock(_HEAP_LOCK); /* block other threads */
    __TRY
    /* get a pointer to memory block header */
    pHead = pHdr(pUserData);
    /* verify block type */
    _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
    _free_dbg(pUserData, pHead->nBlockUse);
    __FINALLY
    _munlock(_HEAP_LOCK); /* release other threads */
    __END_TRY_FINALLY
    return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

       通过上述两个全局函数的实现,我们可以知道,operator new 实际上就是使用malloc函数来申请空间的,如果使用malloc函数申请空间成功就直接返回,否则执行用户提供空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终就是通过free来释放空间的。

       为什么我们要重新封装一下malloc函数呢??因为C++语言是面向对象的语言,但是之前的malloc是C语言的,不具有面向对象的思想。因为malloc在创建空间失败后,我们需要抛出一个异常,但是malloc函数本身是返回0,所以我们需要将malloc函数重新封装满足我们面向对象的思想。封装free函数其实没有什么,为了对称,我们需要进行重新封装free函数。

我们在使用这些函数,我们需要进行配对使用,如果我们将类中的析构函数进行注释,(这里有一些重点)

四、new和delete的实现原理

4.1 内置类型

       如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方在于:new/delete申请和释放的是单个元素的空间,new[] 和 delete[] 申请的是连续的空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

4.2 自定义类型

new的原理:

  • 调用operator new函数申请空间
  • 在申请的空间上执行构造函数,完成对象的构造

delete的原理:

  • 在空间上执行析构函数,完成对象中资源的清理工作
  • 调用operator delete函数释放对象的空间

new T[N] 的原理:

  • 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  • 在申请的空间上执行N次构造函数

delete[] 的原理:

  • 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  • 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

五、定位new表达式

定位new表达式是在已经分配的原始内存空间中调用构造函数初始化一个对象。

使用的格式是:

new(place_address)type 或者 new(place_address)type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景:
       定位new表达式在实际上一般是配合内存池使用的,因为在内存池分配的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示地调构造函数进行初始化。

六、常见的面试题

6.1 malloc/free 和 new/delete 的区别(从用法和原理上分析)

       malloc/free 和 new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

  • malloc和free是函数,new和delete是操作符
  • malloc申请的空间不会初始化,new可以进行初始化
  • malloc申请空间时,需要手动计算空间大小并传递,new只需在其后面跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  • malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常

       申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数和析构函数,而在new申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。

6.2 内存泄露

6.2.1 什么是内存泄露,内存泄露的危害

       什么是内存泄露:内存泄露指的是因为疏忽或者错误造成程序未能释放已经不再使用的内存的情况。内存泄露并不是指的是内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

       内存泄露的危害:长期运行的程序出现内存泄露,影响很大,比如操作系统,后台服务等等,出现内存泄露会导致响应越来越慢,最终卡死。

6.2.2 内存泄露分类(了解)

C/C++程序中一般我们关心两种方面的内存泄露:

堆内存泄露

       堆内存指的是程序执行中依据需要分配通过malloc/calloc/realloc/new等从堆中分配的一块内存,用完后必须通过调用相应的free或者delete删除。假设程序的设计错误导致这部分的内存没有被释放,那么以后这部分的空间将无法再次使用,就会产生Heap Leak。

系统资源泄露

       这种情况指的是程序使用系统分配的资源,比如套接字,文件描述符,管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可以导致系统效能减少,系统执行不稳定。

6.2.3 如何检测内存泄露(了解)

       在VS下,可以使用windows操作系统提供的_CrtDumpMemoryLeaks()函数进行简单检测,该函数只报出了大概率泄露了多少个字节,没有其他更准确的位置信息。

int main()
{
    int* p = new int[10];
    // 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏
    _CrtDumpMemoryLeaks();
    return 0;
}

// 程序退出后,在输出窗口中可以检测到泄漏了多少字节,但是没有具体的位置
Detected memory leaks!
Dumping objects ->
{79} normal block at 0x00EC5FB8, 40 bytes long.
    Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

6.2.4 如何避免内存泄露

  1. 工程前期良好的设计规范,养成了良好的编码规范,申请的内存空间记着匹配的去释放,(这个理想状态,但是如果碰上异常时,就算注意释放了,还是可能会出现问题。需要下一条智能指针来管理才有保证)
  2. 采用RAII思想或者智能指针来管理资源
  3. 有些公司内部规范使用内部实现的私有内存管理库,这些库自带内存泄露检测的功能选项
  4. 出问题了使用内存泄露工具检测(不过很多工具不靠谱)

总结一下:

内存泄露非常常见,解决方案分为两种:

  1. 事前预防型,比如智能指针等
  2. 事后查错型,比如泄露检测工具

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2143351.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Text-to-SQL技术升级 - 阿里云OpenSearch-SQL在BIRD榜单夺冠方法

Text-to-SQL技术升级 - 阿里云OpenSearch-SQL在BIRD榜单夺冠方法 Text-to-SQL 任务旨在将自然语言查询转换为结构化查询语言(SQL),从而使非专业用户能够便捷地访问和操作数据库。近期,阿里云的 OpenSearch 引擎凭借其一致性对齐技术,在当前极具影响力的 Text-to-SQL 任务…

【C++11 —— 异常】

C —— 异常 C语言传统的处理错误的方式C异常概念异常的使用异常的抛出和捕获异常的重新抛出异常安全异常规范 自定义异常体系自定义异常体系的目的 C标准库的异常体系异常的优缺点 C语言传统的处理错误的方式 在C语言中&#xff0c;错误处理通常依赖于返回值和全局变量的方式…

简单了解微服务--黑马(在更)

认识微服务 单体架构 不适合大型复杂项目 微服务架构 将单体结构的各个功能模块拆分为多个独立的项目 拆取的独立项目分别开发&#xff0c;在部署的时候也要分别去编译打包&#xff0c;分别去部署&#xff0c;不同的模块部署在不同的服务器上&#xff0c;对外提供不同的功能…

算法导论(第3版)

目录 第一部分 基础知识第2章 算法基础2.1 插入排序 第二部分 排序和顺序统计量第三部分 数据结构第四部分 高级设计和分析技术第五部分 高级数据结构第六部分 图算法第七部分 算法问题选编第八部分 附录&#xff1a;数学基础知识 第一部分 基础知识 第2章 算法基础 2.1 插入…

【智路】智路OS 服务组件开发

https://airos-edge.readthedocs.io/zh/latest/dev_guide/service_dev.html 1 总览 1.1 功能 感知服务包含感知的整体pipeline&#xff0c;主要模块包括单相机感知和融合。 单相机感知模块 主要功能为接收IP相机RTSP视频流&#xff0c;解码成RGB图片&#xff0c;通过算法识…

【黑马点评】已解决java.lang.NullPointerException异常

Redis学习Day3——黑马点评项目工程开发-CSDN博客 问题发现及描述 在黑马点评项目中&#xff0c;进行到使用Redis提供的Stream消息队列优化异步秒杀问题时&#xff0c;我在进行jmeter测试时遇到了重大的错误 发现无论怎么测试&#xff0c;一定会进入到catch中&#xff0c;又由…

DRS部署(DM8-DM8)

DRS部署 一、规划端口二、设置环境变量三、开启源数据库的归档和逻辑日志四、配置DDL同步五、创建用户六、 DRS服务部署&#xff08;DM8目的端&#xff09;6.1 部署 drs 服务6.2启动drs服务 七、 DRS 服务部署&#xff08;DM8 源端&#xff09;7.1 部署 DRS服务7.2 启动dmhs服务…

C++第七节课 运算符重载

一、运算符重载 并不是所有情况下都需要运算符重载&#xff0c;要看这个运算符对这个类是否有意义&#xff01; 例如&#xff1a;日期减日期可以求得两个日期之间的天数&#xff1b;但是日期 日期没有意义&#xff01; #include<iostream> using namespace std; clas…

SpringBoot启动成功,但端口启动失败

目录 一、问题展示 二、问题分析 2.1.端口与Tomcat的关系 2.2.问题分析 三、SpringBoot常见知识记录 3.1.SpringBoot项目常用jar包 3.1.1.必要性jar包 3.1.2.选择性jar包 3.2.标签的作用及取值 3.2.1.compile&#xff08;编译范围&#xff09; 3.2.2.provided…

爵士编曲:爵士鼓编写 爵士鼓笔记 底鼓和军鼓 闭镲和开镲 嗵鼓

底鼓和军鼓 底鼓通常是动的音色&#xff0c;军鼓通常是大的音色。 “动”和“大”构成基础节奏。“动大”听着不够有连接性&#xff0c;所以可以加入镲片&#xff01; 开镲 直接鼓棒敲击是开镲音色 闭镲 当脚踩下踏板&#xff0c;2个镲片合并&#xff0c;然后用鼓棒敲击&am…

Koa安装和应用

文章目录 1、Koa21.1 简介1.2 安装1.3 简单使用1.4 使用脚手架创建Koa项目 1、Koa2 1.1 简介 Koa 是一个新的 web 框架&#xff0c;由 Express 幕后的原班人马打造&#xff0c; 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async…

rust快速创建Tauri App ——基于create-tauri-app

Tauri App Tauri是一个工具包&#xff0c;可以帮助开发人员使用现有的几乎任何前端框架为主要桌面平台制作应用程序。核心是用Rust构建的&#xff0c;CLI利用Node.js使Tauri成为创建和维护优秀应用程序的真正多语言方法。 cargo install create-tauri-appcreate-tauri-app&am…

多版本node管理工具nvm

什么是nvm&#xff1f; 在项目开发过程中&#xff0c;使用到vue框架技术&#xff0c;需要安装node下载项目依赖&#xff0c;但经常会遇到node版本不匹配而导致无法正常下载&#xff0c;重新安装node却又很麻烦。为解决以上问题&#xff0c;nvm&#xff1a;一款node的版本管理工…

FSFP——专为蛋白质工程设计的少样本学习策略

论文地址&#xff1a;通过小样本学习&#xff0c;以最少的湿实验室数据提高蛋白质语言模型的效率 参考文献&#xff1a;AI蛋白质设计“新引擎”:FSFP驱动大模型超低采样学习,少量数据显著提升蛋白质语言模型的性能 前言介绍&#xff1a;上海交通大学自然科学研究院洪亮教授课…

在STM32工程中使用Mavlink与飞控通信

本文讲述如何在STM32工程中使用Mavlink协议与飞控通信&#xff0c;特别适合自制飞控外设模块的项目。 需求来源&#xff1a; 1、增稳云台里的STM32单片机需要通过串口接收飞控传来的云台俯仰、横滚控制指令和相机拍照控制指令&#xff1b; 2、自制的有害气体采集器需要接收飞…

PCL 曲线点云提取

文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 提取曲线点云的方法主要分为两种:参数化与非参数化,其中参数化是指事先直线曲线的形状,反之,非参数化则是不依赖与曲线的参数,通常是一种聚类的行为。这里我们采用非参数方法(TriplClust),将点集划分为一个未…

Java ETL - Apache Beam 简介

基本介绍 Apache Beam是一个用于大数据处理的开源统一编程模型。它允许用户编写一次代码&#xff0c;然后在多个批处理和流处理引擎上运行&#xff0c;如Apache Flink、Apache Spark和Google Cloud Dataflow等。Apache Beam提供了一种简单且高效的方式来实现数据处理管道&…

上海儿童自闭症寄宿制学校,让孩子找到归属感

在探讨自闭症儿童教育的广阔图景中&#xff0c;上海作为一座充满人文关怀的城市&#xff0c;始终致力于为这些特殊的孩子提供更加全面、专业的支持体系。而当我们把这份关注与努力投射到具体实践上&#xff0c;广州的星贝育园自闭症儿童寄宿制学校便成为了这样一个温馨而有力的…

蓝桥杯DS18B20程序源码

蓝桥杯DS18B20程序源码解析 蓝桥杯&#xff0c;作为一项全国瞩目的电子设计竞赛&#xff0c;其核心挑战在于参赛者需深度融合单片机编程与各类电子元件的应用能力。在众多项目中&#xff0c;涉及DS18B20数字温度传感器的程序源码尤为引人注目&#xff0c;它巧妙地将单片机技术…

ollama安装(ubuntu20.04)

Ollama是一款开源的自然语言处理工具&#xff0c;它可以帮助开发者快速构建文本处理应用。 ollama官网: https://ollama.ai/ 一、ollama 自动安装 linux统一采用sh脚本安装&#xff0c;一个命令行搞定。 curl -fsSL https://ollama.com/install.sh | sh二、ollama 手动安装 o…