C++内存分布 new和delete介绍

目录

C/C++内存分布

栈区

堆区

静态区

常量区

C++   new和delete

分配空间形式对比

new  delete与malloc  free的区别

可不可以串着使用new和free呢


C/C++内存分布

C++的内存分布,大体上分为栈区 堆区  静态区 常量区

栈区

栈区是用于存储函数调用时的局部变量 函数参数 返回地址等,当一个函数被调用的时候,栈区会分配一块空间给这个函数,其局部变量会被分配到栈上,函数执行结束后,这些局部变量的内存空间会被释放。栈内存是自动管理的,不需要手动分配或释放。常见的存栈区的有局部变量,函数,函数调用的参数,函数的返回地址以及返回值。请注意编译器产生的临时变量也存在栈区,虽然说它具有常性,但那只是一个权限限制(相当于自带const),它依旧存储在栈区,用完了自己就会销毁。以上的也全适用于main函数。

堆区

堆区是一种动态分配的内存区域,用来存动态分配的对象和数据,直白点讲malloc,calloc,new等动态分配出来的空间都在堆区。堆区最重要的议题就是内存泄漏了,手动申请的堆空间如果不手动free虽然程序结束这段空间会自动,但是资源没回收会发生内存泄漏。

  内存泄漏举个例子,出去旅行在酒店开了房(这个房间就是我malloc申请的空间),但是我行李箱(行李箱就是资源,其实就是这段空间定义的变量之类的东西,这些变量也会使用申请的空间)忘拿走扔在酒店直接退房走了。我退房了我对这个房间就没有使用权了,我的行李箱也许还在原地也许已经被人拿走了。现实生活中肯定会回去找酒店帮忙取出来行李箱,但是对于计算机来说这片空间已经没有使用权了,也就没办法取出行李箱(资源)了,此时行李箱可能会扔在酒店的某个地方(反正没人管了),行李箱也会占用空间的,如果遗留的行李箱(资源)越来越多,终有一天会把整个空间占满,导致下一次手动申请空间空间会不足。

  我只是举个例子帮助理解,现实生活中肯定不会像上面那样。

  总的来说内存泄漏就是指那些已经被分配但未被正确释放(即未处理)的内存资源。这些资源在逻辑上已经不再被程序所使用,但由于没有调用相应的释放函数(如free在C语言中),malloc申请的空间虽然程序结束会自动收回,但是这片空间里的资源(变量什么的)不会清理,它们仍然占据着物理内存空间。由于这些未释放的资源仍然占用空间,如果程序持续运行且不断产生新的内存泄漏,这些闲散资源确实会越积越多。在堆(heap)上分配的内存空间是有限的,因此,如果泄漏的内存量持续增长,最终确实有可能将堆空间占满。当堆空间被占满时,程序将无法再成功分配新的内存块,这可能会导致程序崩溃或表现出不可预测的行为。

      为什么内存泄漏不会报错呢,内存泄漏相当于慢性病,平时可能都看不出来,但不表示没有问题。等变得严重了,身体受不了了才会给你反应,但是到那时候可能就已经晚了

       那么指针悬挂有又是什么呢,指针变量是保存地址的变量,解引用才是访问这片空间。malloc申请空间时一般会使用指针变量来保存这片空间的地址,如果你已经free这块空间,但是指针变量没有手动置为空,那么就产生了指针悬挂。指针悬挂不会直接报错,只是风险特别大,只有使用的时候才会报错。如果你free完空间后指针变量没有置为空,那么接下来不小心又使用了这个指针变量,进行解引用想要访问指针变量保存地址的那块空间时就会程序崩溃。因为空间已经回收了,你还要访问一个你已经没权限访问的空间(这片空间可能已经被系统回收并分配给其他程序或变量使用,或者根本就不再属于程序的可用内存范围),这就意味着已经退了房,但是房卡忘记还了,有一天又偷偷拿房卡回这个房间看看,这样就会发生不可预估的问题。所以已经释放空间的指针变量手动置空比较好,减少风险

静态区

全局变量和静态变量存储在静态存储区中,静态变量在整个程序的整个执行过程都存在,但是静态成员函数不在静态区,静态成员函数和别的成员函数都一样放在公共代码段里,这个代码段是指程序的一部分,包含了程序的所有指令。静态区的东西作用域仅限于定义它们的文件或函数,虽然作用域在函数里,但是这只是说在别的函数里不能使用在这个函数里定义的静态的局部变量,但是它已经存在没有销毁,等程序结束才会销毁。

静态区被称为静态的主要原因是因为它的内存分配和释放是在程序运行期间静态确定的,即在程序启动时就会被分配内存,在程序结束时才会被释放。静态区中的数据在程序整个执行过程中保持不变,不会随着函数的调用或代码块的结束而被销毁,因此被称为静态。

加static的变量基本都是存静态区的,全局变量也在这里,全局的指针变量也在静态区。

常量区

常量数据(如字符串常量)存储在常量存储区中。这些数据在程序运行期间保持不变,通常是只读的。常量存储区的数据在程序启动时被加载,直到程序结束时才被释放。

来看一道题目

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);
}
1. 选择题:
选项 : A . B . C . 数据段 ( 静态区 ) D . 代码段 ( 常量区 )
globalVar 在哪里? ____ staticGlobalVar 在哪里? ____
staticVar 在哪里? ____ localVar 在哪里? ____
num1 在哪里? ____
char2 在哪里? ____ * char2 在哪里? ___
pChar3 在哪里? ____ * pChar3 在哪里? ____
ptr1 在哪里? ____ * ptr1 在哪里? ____
2. 填空题:
sizeof ( num1 ) = ___ ;
sizeof ( char2 ) = ____ ; strlen ( char2 ) = ____ ;
sizeof ( pChar3 ) = ____ ; strlen ( pChar3 ) = ____ ;
sizeof ( ptr1 ) = ____ ;
3. sizeof strlen 区别?

globalVar全局变量在静态区,常量才放常量区。 staticGlobalVar用static修饰,全局的静态变量放在静态区。静态区不只放静态变量还放全局变量

staticVar局部静态变量依旧在静态区。localVar局部变量放到栈里面。char2在栈区,虽然它存字符串常量,但是是在栈上为数组 char2 分配了足够的空间来存储字符串 "abcd" 的拷贝。编译器会将字面量字符串 "abcd" 的内容复制到 char2 数组中。这时,char2 数组包含了与字面量字符串相同的内容,但它存储在栈上,而不是常量区。

*char2是数组第一个元素,也就是a,因为此时是相当于把字符串拷贝过来存到数组里,数组在栈上开了空间存这些东西。即使他是常量此时被拷贝过来也是在栈上,受装它的容器限制。但是因为是字符串常性不能修改还是要保证的,所以加const

pChar3在栈区,指针变量本身在栈区,指针变量用来保存地址的,但是它本身也需要空间去存。此时定义在局部函数里(main也算),所以在栈区

*pChar3在常量区,与char2不同的是这是指针直接指向常量区,pChar3保存的是常量区存字符串本体的空间地址,所以*pChar3在常量区

ptr1定义在栈区,所以在栈区,保存的地址是堆区空间的地址,所以*ptr1在堆区

sizeof(num1) 存的是数组,sizeof计算类型或对象在内存中的大小(以字节为单位),这是数组,1个int数占4个字节,这个数组总共10个数(不明显写的自动填充0),所以sizeof(num1)是40

sizeof(char2sizeof计算类型或对象在内存中的大小(以字节为单位),字符串会在末尾存一个\0作为结束标志,所以sizeof(char2)是5。 

strlen(char2)strlen只会算字符串有效字符,\0不会计算进去,所以结果是4

sizeof(pChar3) pChar3是指针,指针在32位下sizeof是4,64位下是8

strlen(pChar3)  strlen只会算字符串有效字符,\0不会计算进去,所以结果是4

sizeof(ptr1)  是个指针,同pChar3

C++   new和delete

new和delete是C++进行动态管理内存的方式,是C语言里malloc等和free的升级版,malloc他们俩能干到的事,new他们都能干到。

分配空间形式对比

单个类型分配空间malloc和free形式

int main()
{int* arr = (int*)malloc(sizeof(int));int* brr = (int*)malloc(sizeof(int)*10);//分配多个int类型的空间if (arr == NULL){perror("malloc fail");//内存分配失败打印exit(1);//退出}if (brr == NULL){perror("malloc fail");//内存分配失败打印exit(1);//退出}free(arr);//释放空间arr = NULL;//手动置空,防止悬挂指针brr=NULL;
}

new和delete形式

int main()
{int* arr = new int;int* brr = new int[10];//分配多个类型的空间delete arr;delete[] brr;arr = NULL;brr = NULL;
}

new  delete与malloc  free的区别

1.对于自定义类型new会自动调它的构造函数,delete会自动析构函数,而malloc和free不会

而内置类型基本是一样的

malloc的情况,不会调用构造函数

 new和delete的情况

new A[3]相当于构造了三次对象,相当于对象数组,所以可以通过数组的方式来访问他们,brr[0],brr[1],brr[2]

析构这个对象数组不是用delete brr[],这样会报错的,而是delete [] brr;

2.malloc分配空间返回空所以一定要判空,new会抛异常需要捕获异常

在C语言中,malloc函数用于动态内存分配,它返回一个指向所分配内存的指针,如果内存分配失败,则返回NULL。由于C语言不支持异常处理机制,因此malloc需要程序员显式地检查其返回值是否为NULL,以确定内存是否成功分配。这是C语言处理错误的一种常见方式——通过返回值或特殊错误码来指示函数调用的成功或失败。

而在C++中,new操作符被设计为在内存分配失败时自动抛出std::bad_alloc异常。C++的异常处理机制允许程序在运行时遇到错误情况时,通过抛出和捕获异常来优雅地处理这些错误。因此,当new无法分配所需内存时,它会自动触发异常处理流程,而不需要程序员显式地检查返回值。

区别在于C语言返回的是错误码,而且你用malloc每次开一次空间就要写一次判断错误码是什么,然后要手动进行退出。而C++是自动抛出异常,他们会自动退出。

如果你捕获了std::bad_alloc异常,你可以选择在捕获块中打印任何你想要的信息。通常,你可能会打印一条错误消息,告知用户或开发者内存分配失败的情况。例如:

try {  int* largeArray = new int[SOME_LARGE_NUMBER];  // ... 使用largeArray ...  delete[] largeArray;  
} catch (const std::bad_alloc& e) {  std::cerr << "内存分配失败: " << e.what() << std::endl;  // 可以在这里添加其他错误处理代码  
}

那么new究竟是怎么来实现开空间的呢,可以通过反汇编来看一下

call是调函数的意思,通过反汇编可以看出来new其实调了一个operator new的函数(这个不是重载),operator new函数内容如下,可以看出operator new实际上底层依旧是malloc,也需要类型大小,也需要强转类型,但是多了抛异常

 其实operator new因为底层是malloc,而operator delete 是free,所以他们其实也可以直接使用

定位new表达式(placement-new)

但是 为什么不会打印构造函数和析构函数字样呢,因为operator new他们也不会走构造和析构,但是new会。总结一下malloc的功能其实就只是开空间而已,而operator new不只可以开空间,而且可以抛异常,而new不只能通过operator new来开空间和抛异常,而且可以完成析构。那么究竟在operator new上面加了什么以用来调构造函数呢。直接通过开辟空间地址的指针arr->A去访问是不行,可以试一下但是基本都是报错。

这就引出了定位new表达式,定位new表达式是在已分配的原始内存空间中调用构造函数的表达式

使用格式:
new (place_address) type 或者 new (place_address) type(initializer-list)
place_address 必须是一个指针, initializer-list 是类型的初始化列表
使用场景:
定位 new 表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义
类型的对象,需要使用 new 的定义表达式进行显示调构造函数进行初始化。

多重对象数组开空间构造定位new表达式是通过偏移量来访问各个对象空间来进行构造和析构的

 为什么构造不能用arr->A(1),而析构可以直接这样呢,虽然可能很多人会说这是规定,然后就不了了之,我还是想说得清楚些

在C++中,构造函数和析构函数的使用方式确实有所不同,这主要源于它们各自的目的和生命周期管理的差异。

构造函数用于初始化对象,它在创建对象时自动调用。当你使用new关键字创建一个对象时,C++会首先分配内存,然后调用对象的构造函数来初始化这块内存。构造函数没有返回值(也不是void),因为它们负责构建对象本身,而不是执行某个操作并返回结果。因此,你不能直接通过指针来调用构造函数,因为构造函数的调用是与对象的创建紧密绑定的。

相比之下,析构函数用于销毁对象并清理资源。当对象的生命周期结束时(例如,当对象离开其作用域或被delete释放时),析构函数会自动调用。你可以通过指针显式地调用析构函数,因为这时你已经有了一个存在的对象,你只是想提前结束它的生命周期。通过指针调用析构函数的形式是ptr->~TypeName(),其中ptr是指向对象的指针,TypeName是对象的类型。这种显式调用通常与定位new(placement new)一起使用,当你手动管理对象的内存时,需要显式地调用析构函数来执行清理操作。

可不可以串着使用new和free呢

对于内置类型来说new和free是可以串着用的,但是不推荐串着用,因为有可能发生奇怪的问题

对于 内置类型是不行的,一方面malloc和free调不了构造函数和析构函数,一方面在于free可能会极大可能造成内存泄漏

为什么呢,对于delete来说分为两个步骤,operator delete这个底层是free,另一方面它还会调析构

 如果类里面的成员变量是别的类类型并且涉及到要另外malloc开空间的话,调这个类的析构函数函数的同时会自动调这个类类型成员的析构函数把它的空间资源先析构了,然后是析构类。如果是free会直接把类的空间释放了,但是这个类也可能有类成员开了空间啊,free不会考虑这个,这就造成了内存泄漏。

比如说类与对象里面写的函数两个别的类类型stack的类queue

总结一下

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 在释放空间前会调用析构函数完成空间中资源的清理

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

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

相关文章

MySQL——运维

日志 错误日志 错误日志是 MySQL 中最重要的日志之一&#xff0c;它记录了当 mysqld 启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 查看日志位置&#xff1a; sho…

Eigen::svd和 np.linalg.svd的不同之处

目录 pythonc结论参考 SVD奇异值分解与PCA主成分分析 SVD动画图解–Wiki Eigen Svd 和 np.linalg.svd都可以用于SVD计算&#xff0c;但两者却存在细微的差别。 python import numpy as np datanp.array([[0.99337785, 0.08483806, 0.07747866, -92.91055059],[-0.07889607,…

【Qt常用控件】—— 多元素控件

目录 1.1 List Widget 1.2 Table Widget 1.3 Tree Widget 1.4 小结 Qt 中提供的多元素控件有: QListWidget QListView QTableWidget QTableView QTreeWidget QTreeView xxWidget 和 xxView 之间的区别 以 QTableWidget 和 QTableView 为例&#xff1a; QTableView 是基于…

03-JAVA设计模式-备忘录模式

备忘录模式 什么是备忘录模式 Java中的备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许在不破坏封装性的前提下捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态&#xff0c;以便以后可以将对象恢复到原先保存的状态…

Idea:阿里巴巴Java编码插件

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、Alibaba Java Coding Guidelines插件介绍 二、使用步骤 总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、Alibaba Java Coding …

【AI】Deepstream入门(2)Ubuntu20.04安装Deepstream

1、安装GPU驱动 本人显卡型号:RTX4060 Laptop(笔记本专用显卡) 【AI】惠普暗夜精灵9安装Ubuntu20.04+nvidia驱动 2、安装cuda、cuDNN 【AI】Ubuntu20.04安装cuda、cuDNN 3、安装TensorRT 1)下载 下载地址:https://docs.nvidia.com/deeplearning/tensorrt/archives/i…

用于肺结节分类的常规 EHR 的纵向多模态Transformer集成成像和潜在临床特征

Longitudinal Multimodal Transformer Integrating Imaging and Latent Clinical Signatures from Routine EHRs for Pulmonary Nodule Classification 摘要 该研究提出了一种基于Transformer 的多模态策略&#xff0c;用于将重复成像与常规电子健康记录&#xff08;EHRs&…

低空经济概念

低空经济是指利用低空空域资源&#xff0c;通过低空交通工具和技术创新发展&#xff0c;促进航空产业、旅游、物流、紧急救援等多领域经济增长和产业融合。随着科技的不断进步和航空产业的快速发展&#xff0c;低空经济正逐渐成为全球经济的重要组成部分。 一、低空经济的主要特…

「珞石机器人」完成超5亿元战略+轮融资

珞石机器人ROKAE. 新一代智能机器人专家 近日&#xff0c;襄禾资本投资企业「珞石机器人」宣布完成超5亿元的战略轮融资&#xff0c;本次融资获得了国家制造业转型升级基金和邹城市新动能产业投资基金的共同加持&#xff0c;资金将主要用于市场开发、国际化开拓、产品升级迭代…

【【gitlab解决git Clone 出现 Permission denied, please try again.】】

【gitlab解决git Clone 出现 Permission denied, please try again.】 问题解决随便找一个地方 点击右键输入ssh -keygen -C "邮件"显示结果输入 登录gitlab然后再次git Clone就可以了。 问题 git clone的时候出现 Permission denied, please try again 解决 随便…

怎么使用下载视频号视频?详细视频下载使用教程

越来越多的人开始使用视频号等平台来分享和观看视频内容。然而&#xff0c;有时候我们可能会遇到需要将视频保存到本地设备以便离线观看或进一步编辑的情况。 本文将为您详细介绍如何使用视频下载plus&#xff0c;来下载视频号的视频内容。 一、了解视频号下载功能 首先&…

http忽略ssl认证

我们在发请求时&#xff0c;会遇到需要ssl证书验证的报错&#xff0c;针对该错误以及所使用的不同的创建连接的方式&#xff0c;进行ssl证书忽略 忽略SSL证书的流程 简介&#xff1a;需要告诉client使用一个不同的TrustManager。TrustManager是一个检查给定的证书是否有效的类…

【可下载】CDA 1级教材《精益业务数据分析》2023最新版

CDA一级认证教材&#xff1a;《精益业务数据分析》 全面、系统地讲述业务描述性分析为企业决策行为创造价值的全流程技能&#xff0c;涵盖描述性数据分析方法、业务分析方法、数据分析结果应用方法等内容。 条理清晰的结构、通俗易懂的语言、完整立体的知识框架为读者铺开一幅…

python逆向基础流程(纯小白教程)

一&#xff0c;例题链接 NSSCTF | 在线CTF平台 二&#xff0c;文件特征 使用工具查看文件信息&#xff0c;发现是pyinsatller打包的exe文件&#xff0c;如果硬用ida分析成汇编或c语言根本摸清楚程序的逻辑&#xff0c;所以思路是反编译成py文件直接分析python代码 三&#xf…

【go零基础】go-zero从零基础学习到实战教程 - 2项目初始化

到项目初始化过程了&#xff0c;这边的项目设计完全按照作者自己的喜好来进行定义和设置的&#xff0c;所以各位完全可以按照自己的偏好自喜设置哈。 首先是创建一个工作文件夹哈。 别问为啥不直接quickstart&#xff0c;因为quickstart生成的api名字是greet&#xff0c;改起来…

【EMQX】使用websocket订阅EMQX数据

需求&#xff1a;某平台希望通过 websocket 来订阅 EMQX平台上的某些 Topic数据进行处理 1、EMQX 服务配置 前提是EMQX服务正常安装运行了&#xff0c;如果EMQX服务未安装的话&#xff0c;详见以下文章关于如何安装部署服务&#xff1a; 搭建自己的MQTT服务器、实现设备上云(W…

uniapp-css:拼图(不规则图片拼插)、碎片

拼图案例样式 高斯模糊的地方可以对应的使用fliter属性和opacity来调节样式。 其余碎片和图片对应: 这段代码实现了一个拼图效果的Vue组件。以下是对代码的详细解析: 模板部分: 在模板中使用v-for指令遍历imgs数组中的每个图片对象,为每个图片创建一个元素。 使用:cla…

实验一: 设备密码配置与远程管理

1.实验环境 用路由器和交换机搭建实验环境 2.需求描述 实现管理员主机对交换机和路由器的远程管理 设备上配置的密码都要被加密 3.推荐步骤 对路由器配置的步骤如下&#xff1a; 实现路由器和PC的连通性配置VTY密码和特权模式密码在PC上Telnet 到路由器。 对交换机配置的…

柏曼和琪朗护眼灯哪个好?书客、柏曼、琪朗护眼大路灯横测

护眼大路灯是一种备受用户认可的照明电器&#xff0c;但是市面上众多大路灯品牌的评价都良莠不齐&#xff0c;外观几乎清一色的大路灯在品质上却有着很大的区别&#xff0c;很多朋友想要入手但是却迟迟不敢下手。那么&#xff0c;怎么才能买到性能优越、各方面又出色的大路灯呢…

JAVA实现easyExcel模版导出

easyExcel文档 模板注意&#xff1a; 用 {} 来表示你要用的变量 &#xff0c;如果本来就有"{“,”}" &#xff0c;特殊字符用"{“,”}"代替{} 代表普通变量{.}代表是list的变量 添加pom依赖 <dependency><groupId>com.alibaba</groupId&g…