new,malloc区别的深度解析
Posted 浩瀚之水_csdn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了new,malloc区别的深度解析相关的知识,希望对你有一定的参考价值。
1. 关键字、操作符与库函数
- 关键字是编译器保留的文字,不能被用户拿来重新声明,像const, new, if等等
- 操作符必须要有操作对象,操作符本质上可以视为编译器内置的基础的函数。操作符在c++中,可以被重载(除了部分例外,比如 . :: sizeof)。
- 库函数是编写在编译器头文件库里,要包含头文件才能调用的封装函数。
2. 自由存储区与堆的区别
堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。我们所需要记住的就是:
堆是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。
3. 野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
4. malloc和new区别
在C++中,申请动态内存与释放动态内存用new/delete 与 malloc/free都可以,new/malloc申请动态内存,操作系统无法自动回收,需要对应的delete/free释放空间。
- malloc/free 是c语言中的库函数,需要头文件支持;而new/delete 是c++中的关键字
- 使用new操作符时编译器会根据类型信息自行计算所需要的内存,而malloc需要显式指出所需内存的尺寸
int *p = new int[2];
int *q = (int *)malloc(2*sizeof(int));
-
malloc内存分配成功时,返回的是void*,需要转换成所需要的类型
new内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符
free释放内存的时候需要的是void* 类型的参数
delete释放内存的时候需要使用具体类型的指针
-
new操作符在分配失败的时候会抛出bac_alloc异常
malloc在分配内存失败时返回NULL
-
new操作符从自由存储区(free store)上为对象动态分配内存空间,允许重载new/delete操作符
malloc函数从堆上动态分配内存,malloc不允许被重载
-
new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
5. new()和new[]的区别
- new()创建一个对象
- new[]创建一个动态数组
- 释放方法也不一样,new()对应delete,new[]对应delete[]
char *pc = new char('a'); //开辟一个内存单元,并用括号里的初始化
char *pca = new char[15]; //开辟一个数组
释放内存的方法也不一样:
delete pc;
delete []pc;
6. delete和delete[]的区别
new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[],其实要分基本数据类型和自定义数据类型。
-
基本数据类型
int *a = new int[10];
...
delete a; // 方式1
delete [ ] a; //方式2
基本的数据类型对象没有析构函数,并且new 在分配内存时会记录分配的空间大小,则delete时能正确释放内存,无需调用析构函数释放其余指针。因此两种方式均可。
- 自定义数据类型
#include <iostream>;
using namespace std;
class T {
public:
T() { cout << "constructor" << endl; }
~T() { cout << "destructor" << endl; }
};
int main()
{
const int NUM = 3;
T* p1 = new T[NUM];
cout << hex << p1 << endl; //输出P1的地址
// delete[] p1;
delete p1;
cout << endl;
T* p2 = new T[NUM];
cout << p2 << endl; //输出P2的地址
delete[] p2;
return 0;
}
从运行结果中我们可以看出,delete p1 在回收空间的过程中,只有 p1[0] 这个对象调用了析构函数,其它对象如 p1[1]、p1[2] 等都没有调用自身的析构函数,这就是问题的症结所在。如果用 delete[],则在回收空间之前所有对象都会首先调用自己的析构函数。
基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。对于 new 的单个对象,只能用 delete 不能用 delete[] 回收空间。
所以一个简单的使用原则就是:new 和 delete、new[] 和 delete[] 对应使用。
7. delete或者free之要将指针指为NULL
delete和free被调用后,内存不会立即回收,指针也不会指向空,delete或free仅仅是告诉操作系统,这一块内存被释放了,可以用作其他用途。但是由于没有重新对这块内存进行写操作,所以内存中的变量数值并没有发生变化,这时候就会出现野指针的情况。因此,释放完内存后,应该把指针指向NULL
#include <iostream>
using namespace std;
int main()
{
int *p = new int;
*p = 3;
cout << *p << endl;
cout << p << endl;
delete p;
cout << *p << endl;
cout << p << endl;
return 0;
}
delete之后,p指向的地址并没有发生变化,而地址里面的内容却成了随机数,那么,我们可以得到下面这条结论
我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身。
此时p也就成为一个野指针。
#include <iostream>
using namespace std;
int main()
{
int *p = new int;
delete p;
p = NULL;
delete p;
}
如果不加p=NULL;程序运行时就会挂掉,加上就不会了,对NULL空间多次释放时没有问题的,但是不是NULL就不可以,因为这段空间已经释放掉,不属于本程序,不能够取随意的释放。
8. 使用原则
- 原则1: 优先使用new,delete
- 原则2: 要new和delete配对,new[]和delete [],malloc和free配对使用
- 原则3: free和delte后指针一定赋予NULL,防止成为野指针
参考文献:
以上是关于new,malloc区别的深度解析的主要内容,如果未能解决你的问题,请参考以下文章