1. malloc/free和new/delete之间关系和差异
(1)在C语言中,使用malloc/realloc/calloc/free进行动态内存管理。其中,malloc/realloc/calloc用于动态内存开辟,free用于内存释放。
malloc —— 原型为void* malloc(size_t size);在内存中的动态存储区中分配一个长度为size的空间,返回指向大小为size的内存区域首地址的void指针;用户必须决定对象的长度,即申请空间的大小。同时,malloc只是分配了一块返回值为void*的内存而不是生成一个对象。由于malloc返回值的类型是void*,所以在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型,malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数,因此使用sizeof是很好的方式,例如:int* p = (int*) malloc(sizeof(int) * length);使用malloc所分配的是一块连续的内存,同时由于编译器的实现问题(比如边界对齐等),其所分配的内存可能比所请求的多一点。
如果内存池为空,或者可用内存不能满足所请求的内存,则编译器向操作系统请求,要求得到更多的内存,并在所得到的内存上执行分配任务。若操作系统无法向malloc提供更多的内存,那么malloc返回一个NULL指针,因此有必要对malloc的返回指针进行检查。
realloc —— 原型为void realloc(void* ptr, size_t sz);用于修改一个原先已经分配内存块的大小,可以扩大也可以缩小。若扩大时,则保留原先的内存,将新添加的内存放于原先内存块的后面,新内存并未以任何方式进行初始化;若缩小时,该内存块尾部的部分内存释放,其余部分保留。
注意:若realloc的第一个参数为NULL,那么其行为与malloc一样。另外如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新块上,因此在使用realloc之后就不能再使用指向原内存的指针,而应该改用realloc所返回的新指针。
calloc —— 原型为void* calloc(size_t elem_num, size_t elem_sz);与malloc的主要区别在于calloc在返回指向内存的指针之前把它初始化为0,另一个区别是他们请求内存数量的方式不通,calloc的参数包括所需元素的数量和每个元素的字节数,由此它能够计算出总共需要分配的内存。
-free —— 原型为void free( void* p ); 由于指针p的类型以及它所指的内存的容量事先都是知道的,因此语句free(p)能正确地释放内存。如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致未定义的结果。
下面给出一段代码:
void test()
{
int* p1 = (int*)malloc(sizeof(int)*4);
int* p2 = (int*)calloc(4, sizeof(int));
int* p3 = (int*)realloc(p2, sizeof(int)* 6);
free(p1);
free(p3);
}
(2)在C++语言中,new用于动态内存开辟,delete用于内存释放。
由于对象在创建的时候要调用构造函数进行初始化,而在对象在消亡的时候要调用析构函数进行一些清理工作,显然,malloc/free无法完成这些事情。因此,在C++中提供了new/delete两个操作符,用new在动态分配内存的时候调用构造函数进行初始化,delete在释放内存的时候自动调用析构函数进行清理。
下面写出一个代码:
void test()
{
int* p4 = new int;//4个字节动态分配4个字节(1个int)的空间单个数据
int* p5 = new int(3);//动态分配4个字节(1个int)的空间并初始化为3
int* p6 = new int[3];//动态分配12个字节(3个int)的空间
delete p4;
delete p5;
delete[] p6;
}
new和delete的内部实现
- new通过调用下面的函数实现:
char * operatoer new(size_t count);
new做了两件事:
①调用operator new分配空间。
②调用构造函数初始化对象。
- delete通过调用下面的函数实现:
void operator delete(char *p);
delete做了两件事:
①调用析构函数清理对象。
②调用operator delete释放空间。
(3)总结malloc/free和new/delete的区别与联系
- malloc/free和new/delete都是动态内存管理的入口。
- malloc/free是C/C++标准库函数;new/delete是C++操作符。
- malloc/free只是动态分配内存空间/释放空间;new/delete除分配空间还调用构造函数和析构函数进行初始化与清理。
- malloc/free需要手动计算类型大小且返回值为void*;new/delete可自己计算类型大小,返回对应类型指针。
- malloc失败了返回0;new失败了返回抛出异常。
2. new/delete和new[]/delete[]
对比上述的new/delete的用法及所做事情,我们接下来阐述一下new[]/delete[]的具体实现方法。
- new[]通过调用下面的函数实现:
char * operator new[](size_t count);
new[N]做了两件事情:
①调用operator new分配空间。
②调用N次构造函数分别初始化每个对象。
- delete[]通过调用下面的函数实现:
void operator delete[](char *p);
delete[N]做了两件事情:
①调用N次析构函数清理对象。
②调用operator delete释放空间。
3.实现NEW_ARRAY/DELETE_ARRAY宏,模拟new[]/delete[]申请和释放数组
(1)NEW_ARRAY宏——模拟实现new[]
#define NEW_ARRAY(PTR,TYPE,N) \
do \
{ \
PTR = (TYPE*)operator new(sizeof(TYPE)*N + 4); \
(*(int*)PTR) = N; \
PTR = (TYPE*)((char*)PTR + 4); \
for (size_t i = 0; i < N; i++) \
new(PTR + i)TYPE; \
}while (false);
(2)DELETE_ARRAY宏——模拟实现delete[]
#define DELETE_ARRAY(PTR,TYPE) \
do \
{ \
size_t N = *((int*)PTR - 1); \
for (size_t i = 0; i < N; ++i) \
PTR[i].~TYPE(); \
PTR = (TYPE*)((char*)PTR - 4); \
operator delete(PTR); \
}while (false);