首页 > 系统相关 >C++-内存管理

C++-内存管理

时间:2023-09-21 20:32:26浏览次数:52  
标签:malloc 管理 C++ ____ 内存 空间 new delete

今天,和大家分享一些与内存管理相关的知识,本次的内容主要是new和delete的使用。

内存这一块的知识,我们在学习C语言的时候,就有作相对细致的了解。我们现在来写几道题。做一个简单的回顾复习。

内存的分布

我们先来看看,下面一段代码:

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) = ____;

我们来分析一下,首先,是选择题。

globalVar是全局变量,根据我们之前在C语言时学的,全局变量放在静态区,也就是globalVar被放在静态区。

staticGlobaVar是全局变量,同时,它被static修饰。根据我们之前所学,static修饰的变量和全局变量都会被放到静态区。所以,staticGlobaVar应该会被放到静态区。

staticVar是在函数里面的定义的,按常理来说应该会放到栈中。但它前面加了static修饰,所以,它被存放到静态区。

localVar是在函数里定义的,而函数是存放在栈中的,加上它没有static修饰,所以,它会存放在栈中。

num1,char2,pChar3,ptr1和localVar的情况是一样的,都被存放在栈中。

*char2也是存放栈中的,这是为什么呢?char2是数组名,是首元素的地址,而char2这个数组的内容abcd存放在栈中,对数组名char2的解引用得到的是数组的首元素,数组的内容存放在栈中,它的首元素自然也存放栈中,也就是*char2也存放在栈中。 

*pChar3和char很容易混淆,*pChar存放在常量区。*pChar得到内容abcd,这是一个字符串常量,被存放在常量区。 

*ptr1放在堆上,ptr是realloc向内存动态申请空间返回的地址,我们在之前C语言的学习,有说过动态申请的空间是在堆上开辟的,也就是我们申请的空间在堆上。空间在堆上,用这片空间存储的数据,自然也在堆上啦。

以上是选择题的分析,不知道我有没有说明白,如果你对某处还有疑惑的,可以在评论区下写下你的疑惑大家一同探讨一番,亦或者私信我。

下面,我们来看看填空题:

num1是数组名,sizeof(数组名)计算的是一整个数组的大小,num1数组里面虽然只有4个元素,但它是10个int类型的数组,所以,sizeof(num1)的结果为4*10=40。

char也是数组名,它的初始化方式是用字符串来初始化char这个数组,这也就意味着char这个数组不仅存放了字符串的内容,还在数组末尾存放了字符斜杠零‘\0’,也就是说这个数组实际有五个元素。所以,sizeof(char)的结果为1*5=5。

pChar3和ptr1都是指针,指针的大小只有编译器的位数,32位的环境下,指针的大小为4个字节,64位的环境下8个字节。所以,sizeof(pChar3)和sizeof(ptr1)的结果是4or8。

strlen计算的是字符斜杠零'\0'前面的字符串长度(不包括斜杠零)。char2和pChar3指向的字符串的长度都是4,所以,它们的结果的都是4。

C++-内存管理_内存泄漏

C++-内存管理_数组_02

注释:

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

C++内存管理方式

在C语言的学习中,我们认识了malloc/calloc/realloc/free。我们可以使用malloc,calloc和realloc来向堆申请空间,在申请空间后,我们需要进行一系列的检查,来确定我们是否申请内存成功。这些操作十分繁琐,但为了及时确定问题出现的位置,又不能缺少。对此,C++引用了新的方式,去申请释放内存。

new

C++是通过使用new来申请空间,它的使用方式是关键字new加类型。

我们来看一下示范:

C++-内存管理_内存泄漏_03

我们运行一下程序,看看new开辟的空间有没有初始化。

C++-内存管理_内存泄漏_04

从运行结果来看,是一个随机值。

如果你想让new具有像calloc那样会给初始值的功能,可以在类型后面加上括号,在括号中填上初始值。就可以再开辟空间的同时,给一个初始值。

我们来看一下示范:

C++-内存管理_数组_05

从运行结果来看,我们开辟的空间,成功初始化为0了。

new的初始化实际上是开空间+调用构造函数初始化。我们简单定义一个链表来验证一下:

C++-内存管理_初始化_06

C++-内存管理_初始化_07

我们删除初始值看看:

C++-内存管理_数组_08

从图中看,很明显编译器报错了,没有合适的构造函数。这也就说明了,new的功能不是简单的开空间,还会去自动调用构造函数进行初始化。

new不仅可以为单个值开辟空间,也可以开辟一段连续的空间,存放多个值。如果我们需要开辟更多的空间存放多个的值,需要把跟在类型后面的括号改成方括号,在括号中填上相应的个数。

我们来示范一下:

C++-内存管理_数组_09

从运行结果看,我们成功开辟了一段连续的空间,存放了10个int类型的随机值。

前面,我们可以使用括号的方式给初始值。可现在括号的位置被方括号的位置占了,我们用什么方式来初始化呢?

大家还记得数组的的初始化方式吗?我们可以在方括号后面跟大括号,在大括号中,给初始值,以逗号作为间隔。

我们来演示一下:

C++-内存管理_内存泄漏_10

从运行结果来看,我们成功完成了空间的开辟和初始化。

new除了可以开辟空间外,还可以显示调用构造函数。

我们看看下面这段代码:

C++-内存管理_数组_11

我们来运行一下:

C++-内存管理_初始化_12

从运行的结果看,我们不难发现,new的功能发生了变化,head通过new显示调用构造函数完成了初始化。

现在,我们已经知道了如何去申请空间,下面,我们来了解一下,在C++中,如何把申请来的空间归还给系统。

delete

在C++中,我们使用delete来释放空间。

我们来演示一下:

C++-内存管理_内存泄漏_13

delete后面加不加方括号,取决于你是申请了N个某个类型的空间,还是一个某个类型的空间。

再与实际结合一下:

C++-内存管理_初始化_14

operator new和operator delete函数

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

我们可以通过看反汇编来验证:

C++-内存管理_数组_15

C++-内存管理_内存泄漏_16

下面,我们来看一下operator new和operator delete这两个全局函数的源码:

C++-内存管理_内存泄漏_17

C++-内存管理_数组_18

通过观看operator new和operator detele全局函数的源代码,我们不难发现,operator new是通过malloc来实现的,delete是通过free来实现的。也就是说,new是通过调用operator new函数,使用malloc来实现开辟空间的功,delete是通过调用operator delete函数,使用free来实现空间资源的释放的。

C++-内存管理_内存泄漏_19

自知道new和delete分别是由malloc和free实现的后,有一些同学就会这样混用:

C++-内存管理_数组_20

用new来申请空间,用free来释放空间。从图中看,编译器并没有报错。那我们可不可以这样用呢?用new来开空间,用free来销毁空间资源,我们并不能确定它会发生什么,是否能完成资源的释放。所以,原则上我们一定要配对使用,用new申请就用delete来释放,否则,究竟会发生什么,咱也不清楚。

new/malloc和delete/free的区别是常考的面试题,我们一起来做个总结。

new/malloc和delete/free的区别

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

malloc/free和new/delete不同的地方在:

1.malloc/free是函数,new/delete是操作符。

2.malloc申请空间不会初始化,new可以初始化。

3.malloc申请空间需要计算类型的大小,new申请空间只需要后面跟空间的类型即可,对于多个对象,在类型后面加上方括号,在括号中填上个数即可。

4.malloc的返回值是void*,在使用的时候需要强转。new则不需要,因为它会直接返回申请的空间类型。

5.malloc申请空间失败后,会返回NULL,因此使用时需要判空。new不需要,但需要捕获异常。

6.申请自定义类型对象时,malloc只会申请空间,不会调用构造函数,free释放空间不会调用析构函数,new开辟空间后会自动调用构造函数进行初始化,delete释放空间前会自动调用析构函数。

空间的频繁申请使用,不可避免的会出现一些问题。比如说内存泄漏。

内存泄漏

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

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

delete的引用一定程度上减少了内存泄漏的出现。但内存泄漏的方式很多,很难根除,只能靠程序员的细心,才能避免内存泄漏的出现。

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

如果工程比较大,内存泄漏位置比较多,不太好查时一般都是借助第三方内存泄漏检测工具处理的。比如说,在linux下内存泄漏检测:linux下几款内存泄漏检测工具。在windows下使用第三方工具:VLD工具说明。其他工具:内存泄漏工具比较。

我们还可以跟内存隶属区域来划分是什么类型的内存泄露。

内存泄漏分类(仅做了解)

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

堆内存泄漏(Heap leak)

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

系统资源泄漏

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

好了,到这里,我们本次的分享就到此结束了,不知道我有没有说明白,给予你一点点收获。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。

标签:malloc,管理,C++,____,内存,空间,new,delete
From: https://blog.51cto.com/u_15933803/7556854

相关文章

  • C++中文开发【笑】
    娱乐一下,切勿上纲上线。你会不会还在为代码中众多英文单词感到苦恼。现在只需要引入一个库,你就可以进行C++真·中文开发。示例代码:#include"chinesecpp.h"使用命名空间std;整型划分数组(整型指针数组,整型左下标,整型右下标){ 整型主元位置=(左下标+右下标)......
  • Linux文件管理笔记
     一、文件目录和路径在Linux系统中,文件和目录被组织成一个树状的结构,称为文件目录结构。根目录是整个文件目录结构的最顶层,表示为“/”。所有其他目录和文件都是从根目录开始的。文件路径是指从根目录到目标目录或文件的路径。路径可以是绝对路径或相对路径。-绝对路径:从根目录......
  • C++ RAII在HotSpot VM中的重要应用
    RAII(ResourceAcquisitionIsInitialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII的做法是使用一个对象,在其构造时获取资源,在对象生命期控制范围之下......
  • Mysql日志管理
    MySQL的日志默认保存在数据库文件的存储目录(一般为/usr/local/mysql/data/)。也可以修改配置文件,自定义日志文件的保存位置。我这里在编译安装时,数据库文件存储目录设置的是/home/mysql。1.四种日志格式1.1错误日志错误日志,用来记录当MySQL启动、停止或运行时发生的错误信息,默......
  • 企业为什么要实施敏捷管理?敏捷有哪些好处以及敏捷工具推荐
    敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。那企业为什么要进行变革,实施敏捷开发呢?企业进行敏捷开发的原因主要有以下几点:1、拥抱变化敏捷开发的一个重要特点是能够快速响应和适应市场环境的变化。在竞争激烈的市场环境中,产品的需求和方向经常需要调整,传统的大规模......
  • 湖南大学个人项目C++互评
    优点模块化设计:代码有一个良好的模块化设计,其中每个类和函数都有一个特定的目的。可扩展性:由于使用了继承和多态,该设计易于扩展。例如,添加新类型的问题生成器相对简单。用户交互:代码包含用户交互,允许用户登录并选择问题类型和数量。文件操作:代码成功地将生成......
  • C++ RAII在HotSpot VM中的重要应用
    RAII(ResourceAcquisitionIsInitialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII的做法是使用一个对象,在其构造时获取资源,在对象生命期控制范围之下......
  • c++并发编程实战-第4章 并发操作的同步
    等待事件或等待其他条件坐车案例想象一种情况:假设晚上坐车外出,如何才能确保不坐过站又能使自己最轻松?方法一:不睡觉,时刻关注自己的位置1#include<iostream>2#include<thread>3#include<mutex>4usingnamespacestd;56mutex_mtx;7boolbFlag=false;......
  • 《探索C++多线程》:condition_variable源码(一)
    https://blog.csdn.net/hujingshuang/article/details/70596630    现在接着学习关于多线程编程的特征,在这一节,将会了解到多线程中的condition_variable(条件变量)的相关知识。     在头文件<condition_variable>中有两种条件变量的类声明与定义:condition_varia......
  • 那些让我世界观崩塌的c/c++玩法
    if('\0'==0){printf("true");}else{printf("false");}----------------------------------------------inti=false; if('\0'||0||NULL||i){ printf("true&q......