学过数据结构的话,我们想必对动态开辟内存空间的那几个函数已经颇为了解了吧。在我开始自学C++时,又遇到了一个新的开辟内存空间的操作符new,于是我决定写下这篇博客,将之前学的动态开辟内存空间的库函数和new操作符进行比较,希望会对你们有所帮助
目录
1.复习一下三种动态内存开辟函数
2.什么是new操作符和对应的delete操作符
3.本质
4.总结
一。复习一下三种动态开辟内存函数
(1)malloc
malloc 应该是最为熟悉的吧,当我们需要从堆栈动态的开辟出一段空间来存储数据时,我们可以像如下这么进行内存开辟
char *p=(char *)malloc(sizeof(array[0]))
这里开辟的一段内存是连续的,因此数据也是连续存储的。
总结起来就是,malloc负责在堆栈上开辟空间。
(2)calloc
calloc相对malloc来说,多了一个作用那就是在开辟了一段内存空间之后顺便进行了初始化,不过我们一般还是习惯使用malloc开辟空间然后手动初始化,所以calloc用的并不是很多
(3)realloc (也很常用)
realloc 既可以进行内存的开辟,也可以进行内存的压缩,这要视具体情况而定,并且两种情况的处理方式不太一样
第一种:开辟一块更大的内存
举个很简单的例子,如果你的电话本只可以存一百个电话号码,但是现在有两百个号码你必须要存进去,这时候,realloc通常会这么做
1.在当前内存段的最后地址进行内存的查找,如果后面的一段内存空间足够,那么它会直接在后面进行内存开辟,然后把之前的数据拷到新的内存空间中。
2.如果在久内存地址的后面没有足够的内存空间,那么它就会在堆栈中重新寻找一块合适的内存大小,然后进行内存开辟,再把久的数据拷过去。
第二种:缩小内存空间
这种情况一般出现几率比较小,但是也会出现,比方说,我之前开辟了一段可以存进去一千个整数的内存空间,但是我后来又发现用不了那么多,我就想缩小内存,realloc是这么处理的
(1)这里有一个误区就是,大多数人会以为只需要在原先开辟的内存空间缩小到你想要的大小就可以了,因此内存的首地址不会改变,但是实际上并不是这样的。
(2)编译器还是会重新找一段内存空间,当然也得满足malloc的那几种情况,然后再把之前的数据拷过来。
复习完了这几种函数之后,我就介绍一下什么是new操作符吧
二.什么是new操作符和对应的delete操作符
new的用法和上述的集中函数很类似,都需要一个指针来指向将要开辟的这段内存空间,形式如下
int *p = new int [100];
这样就已经成功开辟了一段内存空间,但是一个好的程序猿通常都会加上assert来断言一下是否空间开辟成功,这是一个良好的习惯。
使用new进行空间开辟没有什么大的问题,关键就在内存的撤销上,这里有好几个坑,我必须强调一下。
那就得说说delete怎么用了
delete [ ]p;
这是和上面对应的直接就可以撤销这段内存的,但是一定要记得将指针置空啊,保持良好的编程习惯很重要。它也有很坑的地方呢,你们可要格外小心
int *p=new int [100];
delete p;
像上面这样撤销内存,一定会出现内存泄漏,这个和delete的运行机理有关,我用一张图片来给你详细分析。
现在坑来了,既然new的开辟机制是这样的,那么如果我开辟了一段空间,然后撤销时这样做,那就一定会出现问题
int *p=new int [10];
delete p;
这时候编译器会默认p只是开辟了一个内存空间的整型,它就不会将指针朝前走一位取获得数组大小,这时它会直接删掉第一个元素然后就停止工作,那么所带来的后果就是之后的内存空间找不到了,内存泄漏。所以,我们在使用delete时一定要注意这个细节。
3.本质
之前说了那么多,都是为了这里的铺垫。好了,我要说最重要的部分了,端个小板凳过来认真听啊,哈哈,开个玩笑,我该认真了
问你一个问题,如果我们把malloc和new的撤销内存交换使用会出现报错或者程序崩溃吗?就像下面这样
int * p1=new int [10];
free(p1);
int *p2=(int *)malloc(10*sizeof(int));
delete p2;
我还是先告诉你们答案再告诉你们为什么吧。
第一个是可行的,不会有任何的问题,第二个跑不过,不信你们可以现在先去试试再听我解释咯
原因:
说出来你们可能不会相信,在C++编译器里,new 的处理方式竟然和malloc是一样的,从汇编语言就可以看到,new的内存开辟还是调用了malloc函数,不过这都是封装在编译器内部的,我们使用的时候并不会看到真实情况,
照这样说的话,delete岂不是也是调用了free函数?哈哈,其实就是这样,它们的底层实现都是这样。为什么是这样呢?依我的理解,我觉得编译器将有些操作封装在底层是为了能够提高程序的效率,增加程序的多态性,因为这几种开辟内存的方法使用的地方都是不同的,一定要保持在不同的地方可以有不同的调用。
总结:
我觉得不论使用哪一种开辟内存的方式,我们都应该有一个良好的习惯,一定要记得断言开辟空间会否成功,在撤销空间之后一定要将指针置空。还有一个就是,使用自己熟练地方式去开辟空间,毕竟C++这两种方式都是支持的