今天,和大家分享一些与内存管理相关的知识,本次的内容主要是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。
注释:
- 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
- 堆用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段--存储全局数据和静态数据。
- 代码段--可执行的代码/只读常量。
- C语言中动态内存管理方式:malloc/calloc/realloc/free
C++内存管理方式
在C语言的学习中,我们认识了malloc/calloc/realloc/free。我们可以使用malloc,calloc和realloc来向堆申请空间,在申请空间后,我们需要进行一系列的检查,来确定我们是否申请内存成功。这些操作十分繁琐,但为了及时确定问题出现的位置,又不能缺少。对此,C++引用了新的方式,去申请释放内存。
new
C++是通过使用new来申请空间,它的使用方式是关键字new加类型。
我们来看一下示范:
我们运行一下程序,看看new开辟的空间有没有初始化。
从运行结果来看,是一个随机值。
如果你想让new具有像calloc那样会给初始值的功能,可以在类型后面加上括号,在括号中填上初始值。就可以再开辟空间的同时,给一个初始值。
我们来看一下示范:
从运行结果来看,我们开辟的空间,成功初始化为0了。
new的初始化实际上是开空间+调用构造函数初始化。我们简单定义一个链表来验证一下:
我们删除初始值看看:
从图中看,很明显编译器报错了,没有合适的构造函数。这也就说明了,new的功能不是简单的开空间,还会去自动调用构造函数进行初始化。
new不仅可以为单个值开辟空间,也可以开辟一段连续的空间,存放多个值。如果我们需要开辟更多的空间存放多个的值,需要把跟在类型后面的括号改成方括号,在括号中填上相应的个数。
我们来示范一下:
从运行结果看,我们成功开辟了一段连续的空间,存放了10个int类型的随机值。
前面,我们可以使用括号的方式给初始值。可现在括号的位置被方括号的位置占了,我们用什么方式来初始化呢?
大家还记得数组的的初始化方式吗?我们可以在方括号后面跟大括号,在大括号中,给初始值,以逗号作为间隔。
我们来演示一下:
从运行结果来看,我们成功完成了空间的开辟和初始化。
new除了可以开辟空间外,还可以显示调用构造函数。
我们看看下面这段代码:
我们来运行一下:
从运行的结果看,我们不难发现,new的功能发生了变化,head通过new显示调用构造函数完成了初始化。
现在,我们已经知道了如何去申请空间,下面,我们来了解一下,在C++中,如何把申请来的空间归还给系统。
delete
在C++中,我们使用delete来释放空间。
我们来演示一下:
delete后面加不加方括号,取决于你是申请了N个某个类型的空间,还是一个某个类型的空间。
再与实际结合一下:
operator new和operator delete函数
new和delete是用户用来动态申请空间和释放操作符,operator new和operator delete是系统提供的 全局函数函数,new在底层是通过调用operator new全局函数来申请空间的,delete在底层是通过operator delete全局函数释放空间资源的。
我们可以通过看反汇编来验证:
下面,我们来看一下operator new和operator delete这两个全局函数的源码:
通过观看operator new和operator detele全局函数的源代码,我们不难发现,operator new是通过malloc来实现的,delete是通过free来实现的。也就是说,new是通过调用operator new函数,使用malloc来实现开辟空间的功,delete是通过调用operator delete函数,使用free来实现空间资源的释放的。
自知道new和delete分别是由malloc和free实现的后,有一些同学就会这样混用:
用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