动态内存分配- new/delete 和malloc/free的区别

来源:互联网 发布:购物返利软件 编辑:程序博客网 时间:2024/05/16 05:05

这边文章参考了C++ primer第五版,郭神的blog,s神的blog,对他们的辛勤劳动成果表示感谢。
1.malloc/free

1.1 malloc()函数
动态存储器分配器维护着一个进程的虚拟存储器区域,称为heap(堆)。系统之间细节不同,但是不失通用性。我们假设堆是一个请求二进制零的区域,它紧接着在未初始化bbs(未初始化数据区域)区域开始,并向上生长(向更高的地址)。堆和栈的生长方向是相反的。对于每一个进程,内核维护者一个变量brk,它指向堆的顶部。

malloc的全称是memory allocation,中文叫动态内存分配。
说明:分配长度为num_bytes字节的内存块,这个一个无符号的整型。如果分配成功则返回指向被分配内存的指针,分配失败返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。

void *malloc(unsigned int size); 

我们一般使用malloc()向系统申请分配指定size个字节的内存空间,返回类型是 void* 类型。void* 表示未确定类型的指针。C/C++中规定,void* 类型可以强制转换为任何其它类型的指针。在这里,void* 表示未确定类型的指针,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据,比如说我们要用这段空间去储存int或者char类型的数据:

int *malloc(unsigned int size);char *malloc(unsigned int size);

1.2 free()函数

void free(void *FirstByte);

是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。

1.3注意事项
(1)申请了内存空间后,必须检查是否分配成功。

int *p=(int *)malloc(unsigned int size); if(p==NULL){    printf("error:your memory allocation didn't work"); }

(2)当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。

free(p);p=nullptr;

(3)这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会出现错误。
比如说我们有两个指针指向同一个动态分配对象时:

int *q=p;

当我们对其中一个指针进行了free操作,对象的内存就被归还给自由空间了。如果我们随后又free第二个指针,自由空间可能会被破坏。

1.4 malloc()从堆里面获得空间
答案是从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

2.new/delete

先来看一条动态申请申请空间的语句,因为在自由空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针:

string *pi=new string();//初始化为空的stringstring *pii=new string("hello world");//pii的对象为“hello,world”

在这里,我们可以稍微看出 new 和 malloc 还是有点不同的,malloc 申请完空间之后不能对内存进行必要的初始化,而 new 可以对动态分配的对象进行值的初始化。所以 new expression 背后要做的事情不并没有这么简单。在我们用实例来解释 new 背后的机制之前,需要知道 operator new 和 operator delete的原理。

这两个其实是 C++ 语言标准库的库函数,原型分别如下:

void *operator new(size_t);     //allocate an objectvoid *operator delete(void *);    //free an objectvoid *operator new[](size_t);     //allocate an arrayvoid *operator delete[](void *);    //free an array

前面两个均是 C++ 标准库函数,C++ Primer 一书上说这不是重载 new 和 delete 表达式(如 operator= 就是重载 = 操作符),因为 new 和 delete 是不允许重载的。但我还没搞清楚为什么要用 operator new 和 operator delete 来命名,比较费解。我们只要知道它们的意思就可以了,这两个函数和 C 语言中的 malloc 和 free 函数有点像了,都是用来申请和释放内存的,并且 operator new 申请内存之后不对内存进行初始化,直接返回申请内存的指针。

2.1 new 和 delete 背后机制

我们不用简单的 C++ 内置类型来举例,使用复杂一点的类类型,定义一个类 A:

class A{public:    A(int v) : var(v)    {        fopen_s(&file, "test", "r");    }    ~A()    {        fclose(file);    }private:    int var;    FILE *file;};

很简单,类 A 中有两个私有成员,有一个构造函数和一个析构函数,构造函数中初始化私有变量 var 以及打开一个文件,析构函数关闭打开的文件。
我们使用:

class A *pA = new A(10);

来创建一个类的对象,返回其指针 pA。如下图所示 new 背后完成的工作:

这里写图片描述

1.首先需要调用上面提到的 operator new 标准库函数,传入的参数为 class A 的大小,这里为 8 个字节,至于为什么是 8 个字节,需要看看《深入 C++ 对象模型》一书,留着以后看吧。这样函数返回的是分配内存的起始地址,这里假设是 0x007da290。

2.上面分配的内存是未初始化的,也是未类型化的,第二步就在这一块原始的内存上对类对象进行初始化,调用的是相应的构造函数,这里是调用 A:A(10); 这个函数,从图中也可以看到对这块申请的内存进行了初始化,var=10, file 指向打开的文件。

3.最后一步就是返回新分配并构造好的对象的指针,这里 pA 就指向 0x007da290 这块内存,pA 的类型为类 A 对象的指针。

如果想释放掉释放掉申请的类的对象:

delete pA;
0 0
原创粉丝点击