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

C和C++内存管理

C和C++内存管理

  • (一)C/C++内存分布
  • (二)C语言动态内存管理
  • (三)c++内存管理
    • (3.1)new/delete操作内置类型
    • (3.2)new和delete操作自定义类型
  • (四)operator new与operator delete函数
  • (五)new与delete的实现原理
    • (5.1)内置类型
    • (5.2)自定义类型
    • (5.3)不匹配使用(面试可能会考)
  • (六)定位new表达式(placement-new) (了解)
  • (七)malloc/free和new/delete的区别(重点)

(一)C/C++内存分布

在这里插入图片描述
我们可以在任务管理器中查看电脑当前进程:
在这里插入图片描述

下图是在c语言阶段我们学过的内存区域划分,这一块在C++同样适用:
在这里插入图片描述
说明:

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

那我们先根据几个题目来回忆我们c语言中内存如何分配的:
在这里插入图片描述
前五个比较简单,大家一定不能出错
第一个:A,main函数外定义的变量都是静态的,生命周期都是从程序开始到结束
第二个:A,全局静态一样
第三个:A,由static修饰的也是全局的
第四个:A,没有static修饰的都是局部的
第五个:A,这里的数组名代表整个数组,也是在局部的栈里存放
后面六个难度上升了很多:
第六个:A,char2也是数组,并且我是我们主动申请的,所以也在栈
第七个:A,*char2是解引用,这里的char2代表的首元素地址,首元素a在栈上
第八个:A, pchar3是一个指针,不管它的const修饰指向的内容还是指针,指针都存于栈
第九个:D,*pchar3也是指向第一个元素的地址,但是内容部分用const修饰了,所以在常量区
第十个:A,ptr1是一个指针
第十一个:B,ptr1所指向的内容使我们主动开辟的,所以在堆区。
在这里插入图片描述

(二)C语言动态内存管理

C语言中动态内存管理方式:malloc/calloc/realloc/free
这一块在c语言部分讲的很多了,所以我就简单的带大家回忆一下,
malloc:开固定的空间,不初始化。
calloc:开空间,初始化0.
realloc:支持拓展我们原来开的空间。
free:释放我们主动开辟的空间。

【面试题】

  1. malloc/calloc/realloc的区别?
  2. malloc的实现原理? 视频链接:https://www.bilibili.com/video/BV117411w7o2/?spm_id_from=333.788.videocard.0

这一块就这样过了,如果这一块还不懂得,建议回去补一下c语言的课。

(三)c++内存管理

(3.1)new/delete操作内置类型

我们简单得来使用一下new和delete

int main()
{int* p1 = new int;int* p2 = new int[10];//简化了c语言sizeof与强转部分delete p1;delete[] p2;//初始化int* p3 = new int(3);int* p4 = new int[10] {0};//全都初始化为0int* p5 = new int[10] {1, 2, 3, 4};//前四个值初始化delete p3;delete[] p4;delete[] p5;return 0;
}

在这里插入图片描述

(3.2)new和delete操作自定义类型

class A
{
public:A(int a = 0){_a = a;cout << "A()" << endl;}~A(){cout << "~A()" << endl;}void print(){cout << _a << endl;}
private:int _a;
};int main()
{A* p1 = new A;A* p2 = new A(1);delete p1;delete p2;return 0;
}

在这里插入图片描述
大家可以看到c++这块new与delete会自动调用自定义类型的构造和析构函数。
这一块非常的方便我们来演示一下c++里的链表:

struct ListNode
{int _val;ListNode* next;ListNode(int val) :_val(val),next(nullptr){}
};
//c++的struct里的变量与函数均为共有的,这一部分前面讲过
int main()
{ListNode* n1 = new ListNode(1);ListNode* n2 = new ListNode(2);ListNode* n3 = new ListNode(3);ListNode* n4 = new ListNode(4);ListNode* n5 = new ListNode(5);n1->next = n2;n2->next = n3;n3->next = n4;n4->next = n5;return 0;
}

这里是不是相较于c里的初始化啊,插入什么的简单许多。

在这里插入图片描述
总之我们只需要记住在c++的使用过程中使用new,delete就行,因为new包含了malloc,delete包含了free。
此外我们在使用new和时是不是与malloc还有一个差别,那就是没有判断是否申请成功,C++引入了抛异常。
抛异常在c++进阶里才会详细的讲解,这里我们简单的演示一下:

int main()
{try{throw try catchvoid* p1 = new char[1024 * 1024 * 1024];cout << "p1" << endl;void* p2 = new char[1024 * 1024 * 1024];cout << "p2" << endl;void* p3 = new char[1024 * 1024 * 1024];cout << "p3" << endl;}catch (const exception& e){cout << e.what() << endl;//发生了什么错误}return 0;
}void func()
{//throw try catchvoid* p1 = new char[1024 * 1024 * 1024];cout << "p1" << endl;void* p2 = new char[1024 * 1024 * 1024];cout << "p2" << endl;void* p3 = new char[1024 * 1024 * 1024];cout << "p3" << endl;
}int main()
{try{func();}catch (const exception& e){cout << e.what() << endl;//发生了什么错误}return 0;
}

上面是在main函数里申请失败,下面是在调用的一个函数里失败。

void func()
{int n = 0;while (1){char* p1 = new char[1024 * 1024];//1024*1024byte是1Mcout << "p1->" << endl;n++;cout << n << endl;;}
}int main()
{try{func();}catch (const exception& e){cout << e.what() << endl;//发生了什么错误}return 0;
}

这个函数就是来看在32位环境下究竟能申请多少MB
在这里插入图片描述
大家可以看到32位(总共4G)下大概只能申请1.7G左右,所以堆在内存占大部分。

(四)operator new与operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
operator new 与operator delete底层源码如下,这一块大家不需要看的特别明白,

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空               间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)//mallocif (_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_FINALLYreturn;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)//free

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

(五)new与delete的实现原理

(5.1)内置类型

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

(5.2)自定义类型

(1)new的原理

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

(5.3)不匹配使用(面试可能会考)

在这里插入图片描述

上面第一组是可以的,第二组不行,我们可以清楚的看到第二组的free少调用了析构函数,这里有内存泄漏的风险。
那我们来看一个神奇的东西:

class A
{
public:A(int a = 0, int b = 0){_a = a;_b = 1;cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;int _b;
};
class B
{
private:int _a;int _b;
};
int main()
{int* p1 = new int[10];//这里没有调用构造函数,实质上只有mallocdelete p1;//这里虽然不匹配,但是内存也不会出现泄漏,实质这里只使用freefree(p1);A* p2 = new A[10];delete p2;//这里有问题B* p3 = new B[10];delete p3;//这里没有问题return 0;
}

大家自己可以去vs里运行一下,那为什么A有问题,B没有问题呢?

首先我们的A与B大小是不是都是8个字节,那我们都调用了10次,那么10个这样的对象是不是80个字节,但是实际上A的10个对象占了84个字节,B的10个对象占了80字节,多的4个字节是用来存储对象的个数。
在这里插入图片描述
那为什么A会多开4个字节存储个数呢?
这是因为B没有析构函数,所以编译器就给优化了,严格的来说多存储的个数是给delete后面的[]使用的,当类没有析构函数时,就认为没有开辟的空间需要释放,只要我们写了析构,编译器就不会再有优化,那B的大小就变为84,。

这里总结就是一定要匹配使用,不要错配。

(六)定位new表达式(placement-new) (了解)

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
上面什么意思呢?
就是我们可以只开空间,不初始化,也就是malloc的功能。

class A
{
public:A(int a = 0, int b = 0){_a = a;_b = 1;cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;int _b;
};
int main()
{A* ptr1 = new A;//调用了构造A* ptr2 = (A*)operator new(sizeof(A));//不调构造new(ptr2)A(1, 0);//定位new//下面两步合起来等于上面一步delete ptr1;ptr2->~A();//析构函数是可以显示调用的operator delete(ptr2);//下面两步合起来等于上面一步return 0;
}

大家可能认为这有点脱了裤子放屁对吧,但我想说的是—说得对,但它也有他自己的用途,在生活中99%的场景用不到,需要用的场景是内存池、线程池,连接池,这一块不讲,只是给大家说下有这个东西。
在这里插入图片描述

(七)malloc/free和new/delete的区别(重点)

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

  1. malloc和free是函数,new和delete是操作符
  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内存管理 &#xff08;一&#xff09;C/C内存分布&#xff08;二&#xff09;C语言动态内存管理&#xff08;三&#xff09;c内存管理&#xff08;3.1&#xff09;new/delete操作内置类型&#xff08;3.2&#xff09;new和delete操作自定义类型 &#xff08;四&#xff09;…...

axios取消请求

1.使用CancelToken: class RequestHttp {service: AxiosInstance;public constructor(config: AxiosRequestConfig) {// 实例化axiosthis.service axios.create(config);/*** description 请求拦截器* 客户端发送请求 -> [请求拦截器] -> 服务器*/this.service.interce…...

阿里中间件——diamond

一、前言 最近工作不忙闲来无事&#xff0c;仔细分析了公司整个项目架构&#xff0c;发现用到了很多阿里巴巴集团开源的框架&#xff0c;今天要介绍的是中间件diamond. 二、diamond学习笔记 1、diamond简介 diamond是一个管理持久配置&#xff08;持久配置是指配置数据会持久化…...

pyenv -- 一款macos下开源的多版本python环境安装管理工具 国内加速版安装 + 项目venv虚拟环境 pip加速 使用与总结

一个比较方便实用的python多版本环境安装管理工具, 阿里云加速版本 pyenv安装方法: 直接克隆本下面到你的本地目录,然后设置环境变量即可 git clone https://gitee.com/tekintian/pyenv.git ~/.pyenv 环境变量配置 在~/.bash_profile 或者 .zshrc 中增加环境变量 export …...

VitePress 自定义 CSS 指南

VitePress 是一款基于 Vite 和 Vue 3 的静态网站生成器&#xff0c;专为文档编写而设计。尽管 VitePress 提供了丰富的默认主题&#xff0c;但在某些情况下&#xff0c;我们可能需要对其进行更深入的定制以满足特定的视觉需求。本文将详细介绍如何通过覆盖根级别的 CSS 变量来自…...

【舍入,取整,取小数,取余数丨Excel 函数】

数学函数 1、Round函数 Roundup函数 Rounddown函数 取整&#xff1a;(Int /Trunc)其他舍入函数&#xff1a; 2、Mod函数用Mod函数提取小数用Mod函数 分奇偶通过身份证号码判断性别 1、Round函数 Roundup函数 Rounddown函数 Round(数字&#xff0c;保留几位小数)&#xff08;四…...

无线信道中ph和ph^2的场景

使用 p h ph ph的情况&#xff1a; Rayleigh 分布的随机变量可以通过两个独立且相同分布的零均值、高斯分布的随机变量表示。设两个高斯随机变量为 X ∼ N ( 0 , σ 2 ) X \sim \mathcal{N}(0, \sigma^2) X∼N(0,σ2)和 Y ∼ N ( 0 , σ 2 ) Y \sim \mathcal{N}(0, \sigma^2)…...

HCIA--实验五:静态路由综合实验

静态路由综合实验 一、实验内容&#xff1a; 1.需求/目的&#xff1a; 在ensp模拟器中使用四个路由器&#xff0c;并且在路由器上创建loopback接口&#xff0c;相当于连接了一台主机&#xff0c;通过配置静态路由的方式实现全网通。 二、实验过程 1.道具&#xff1a; 4个…...

不同vlan之间的通信方法

1.通过路由器的物理接口 1.给PC1,PC2配置IP地址&#xff0c;网关2.进入交换机配置vlan&#xff0c;交换机所有口都配置access口并绑定vlan3.配置路由器&#xff0c;进入路由器的两个接口配置网关IP和掩码缺点&#xff1a;成本高&#xff0c;每增加一个vlan就需要一个物理端口和…...

java后端框架

框架就是对技术的封装。 本篇博客小博主首先对以后我们要学习的框架进行简单概述&#xff0c;使大家对框架有一定的基本概念。 一.mybatis mybatis就是对jdbc&#xff08;数据库连接&#xff09;进行封装&#xff0c;避免了jdbc中手动设置参数&#xff0c;手动映射结果的操作。…...

如何在Word中插入复选框

如何在Word中插入复选框&#xff1a;详细教程与技巧 在Word中插入复选框是一项非常实用的技巧&#xff0c;尤其是在制作问卷调查、待办事项清单、交互式表单或文档中需要用户进行选择时&#xff0c;复选框不仅能提高文档的功能性&#xff0c;还能显得更加专业。本文将详细讲解…...

Android 源码中jni项目 加载so目录小结

Android 源码中jni项目 加载so目录小结 文章目录 Android 源码中jni项目 加载so目录小结一、前言二、so目录验证测试1、jni so文件错误报错&#xff08;1&#xff09;报错1 - 未找到so文件&#xff1a;&#xff08;2&#xff09;报错2 - so文件中未找到native方法&#xff1a; …...

24/9/6算法笔记 kaggle 房屋价格

预测模型主要分为两大类&#xff1a; 回归模型&#xff1a;当你的目标变量是连续的数值时&#xff0c;你会使用回归模型进行预测。回归模型试图找到输入特征和连续输出之间的关联。一些常见的回归模型包括&#xff1a; 线性回归&#xff08;Linear Regression&#xff09;岭回归…...

【MA35D1】buildroot 编译使用经验

文章目录 芯片介绍Buildroot开发Linux实践环境搭建代码获取编译执行步骤&#xff08;仅适用于我公司产品&#xff09; 后续有需要更改的输出文件目录 芯片介绍 NuMicro MA35D1系列为一颗异核同构的多核心微处理器&#xff0c;适用于高端 Edge IIoT Gateway。它是基于双核 64 位…...

排查 MyBatis XML 配置中的 IF 语句与传值名称不匹配的 Bug

文章目录 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都是在得 前言&#xff0c;在改一个bug得时候发现一个有意思得问题&#xff0c;就是myb…...

数字证书与公钥基础设施

关注这个证书的其他相关笔记&#xff1a;NISP 一级 —— 考证笔记合集-CSDN博客 0x01&#xff1a;数字证书 数字证书是由第三方可信机构&#xff08;一般是证书服务器&#xff09;颁发的数字证书&#xff0c;可以证明身份的可信度。 数字证书具有以下特点以及性质&#xff1a…...

拥抱数智化,JNPF低代码平台如何推动企业转型升级

随着信息技术的飞速发展&#xff0c;企业面临的市场竞争日益激烈&#xff0c;传统的业务流程和管理模式已经难以满足快速变化的市场需求。数智化转型成为企业持续发展的必由之路。在这一过程中&#xff0c;低代码开发平台扮演了至关重要的角色。本文将探讨JNPF低代码平台如何助…...

Linux shell脚本 (十二)case语句_linux awk case语句

​ case工作方式如上所示。取值后面必须为关键字 in&#xff0c;每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后&#xff0c;其间所有命令开始执行直至 ;;。;; 与其他语言中的 break 类似&#xff0c;意思是跳到整个 case 语句的最后。​ 取值将…...

【二等奖成品论文】2024年数学建模国赛B题25页成品论文+完整matlab代码、python代码等(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 【全网最全】2024年数学建模国赛B题31页完整建模过程25页成品论文matlab/python代码等&#xff08;后续会更新「首先来看看目前已有的资料&#xff0c;还会…...

国内快速高效下载 HuggingFace上的各种大语言模型

预先安装&#xff1a; apt install aria2 # sudo apt install aria2apt install git-lfs # sudo apt install git-lfs下载hfd wget https://hf-mirror.com/hfd/hfd.shchmod ax hfd.sh设置环境变量 Linux export HF_ENDPOINThttps://hf-mirror.comWindows $env:HF_ENDPOINT…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...