本文目标
- 简单了解malloc和new的用法
- new和malloc的区别和联系
- new和delete的原理
- 为什么在C++中用new不用malloc
- 探寻new[]和delete[]的原理
new和malloc的用法
C中提供了malloc、realloc和calloc来动态管理内存,通过free来释放内存
这几个函数的用法如下:
int main()
{
int size =100;
int* arr1 = (int*)malloc(size * sizeof(int));//申请size个int的空间
int* arr2 = (int*)calloc(size, sizeof(int));//申请size个int的空间并置0
arr1 = (int*)realloc(arr1, size * 2 *sizeof(int));//重新给arr1申请2倍的size的int空间,原来空间的内容依次拷贝到新空间中
free(arr1);
arr1 = NULL:
free(arr2);
arr2 = NULL;//释放后置空指针是个好习惯,但是这里没有一定要置空
return 0;
}
在C++中则提供了new和delete,具体用法如下:
int main()
{
int* a = new int;//开一个int的空间
int* b = new int(10);//开一个int空间并初始化为10
int* c = new int[10];//开10个int的空间
delete a;
delete b;
delete[] c;
}
需要特别注意的是malloc和free,new和delete, new[]和delete[]一定要配合使用,不能混搭,混搭可能会有很严重的后果(并不是每次都会错误),那就是内存泄漏,所以千万不敢混搭
malloc和new的区别和联系
这个问题是面试的高频考点,曾经刚学习C语言的时候就看到过这个问题,不过老忘,现在再次总结一下。
- new和malloc均是在堆上开辟空间。
- new和delete是C++操作符,malloc和free是C/C++的标准库函数
3.而new在开空间之后调用构造函数,delete在删除空间前会调用析构函数, malloc和free只负责开辟和删除空间。(后面讲) - malloc/free需要手动计算开的大小,而new不需要,会自动计算开辟的空间
- new失败了抛异常,malloc失败了返回0
接下来重点来了,这个联系不能在这一笔带过,结合第三部分探索new的原理一起讲
new和delete的原理
这部分的学习依旧要用汇编,不过我们只需要简单看懂就可以了。
先说结论,new调用了operator new,operator new调用了malloc
首先简单看一下要用的代码,很简单的
int main()
{
int* ia = new int;
delete ia;
return 0;
}
接下来就是执行代码的时候,本来想看一下new进去后是啥样,直接执行居然进不去,所以通过汇编代码进去看看是啥样
首先new调用了operator new
进去operator new
还要跳一次,蛋疼
终于跳到真正的operator new
了
我们通过汇编代码终于发现operator new
其实也是调了malloc
来开空间,但是new失败了抛异常在哪,往下翻
扔一个什么标准bad_alloc,嗯没错了,这就是抛异常,至于原理,还没学,下次学了再告诉你们怎么抛
通过上面的汇编代码我们发现,new和malloc的关系非常密切啊,new就用了malloc,然后看看delete是不是也是这样
首先是调用operator delete
然后,进入operator delete
看看
看来是调了free
没错了,也就是说new和delete与malloc和free其实是密切联系的,但是为什么我们在C++中不直接用malloc和free呢???
为什么在C++中用new不用malloc
为了解决这个问题,我们要从自定义类型入手,也就是类,C++中类的概念很重要,对于类来说,不能简单的开辟空间。
举个例子,如果一个类是管理字符串的,就不是简单地开空间了,我们看图
怎么证明new
调用了构造函数,简单,写个实例测一下就好了
光说不练假把式,按照国际惯例,先上代码:
#include <iostream>
using namespace std;
class String
{
public:
String(const char* str = "")
{
//构造函数
int size = strlen(str);
_str = new char[size + 1];
_capacity = size + 1;
char* cur = _str;
while (*cur++ = *str);
}
~String()
{
//析构函数
delete[] _str;
}
private:
char* _str;
int _size;
int _capacity;
};
int main()
{
String* s1 = (String*)malloc(sizeof(String));
String* s2 = new String;//VS下运行代码,执行到此步按F11
free(s1);
delete s2;
return 0;
}
main函数中new
对象String那句按F11,发现直接出现下图情况
看下执行完毕结果
调用构造函数,然后开空间,也就是说new不但开了空间还调用了构造函数,而由于成员是私有的,也就是说malloc开空间之后无法再去给_str
开空间,这也就是为什么我们不用malloc而用new的原因。
delete则是先调用析构函数,再进行free,如果free(s2),会出现内存泄漏,这是非常可怕的一件事。(截图就不截了,懒)
探寻new[]和delete[]的原理
最后一个问题,首先看代码
String* s = new String[100];
delete[] s;
问题是:为什么new String[100]
的时候给了大小,释放的时候delete[]
却不需要给大小,他是怎么知道要释放这么多的空间的?
这个要一句一句执行代码入手了,执行String* s = new String[100];
的时候发现一个有趣的东西,看图
居然吞了我4个字节,干什么去了,猜测是存储了该指针指向对象的个数,我们把这个四字节的数取出来看看
int* police = (int*)s - 1;//强转一下取出这个4字节的内容
由于修改了代码,分配的空间地址不一样了,这不影响
为了证明这不是巧合,我们修改这四个字节的内容,改大一点150,看会不会报错
修改了之后的结果,扔了个异常出来,本来如果不修改这个空间的值他是不会报错的
这也从侧面说明了这个地方存的值就是对象的个数,当delete时,会取出指针前四个字节的内容,知晓删除多少个对象,然后进行删除,至此,案件告破。
因此,这也告诫我们,new[]/delete[]
,new/delete
,malloc/free
一定要配套使用,不能瞎搭配,否则后果自负