malloc/realloc/calloc
在C语言中我们常用malloc/calloc/realloc动态开辟空间,free释放空间。
那malloc/calloc/realloc又有什么不同?
函数原型如下
void *malloc( size_t size );//参数是开辟空间的字节数
void *calloc( size_t num, size_t size );//第一个参数是元素个数,第二个是每个元素所占字节数
void *realloc( void *memblock, size_t size );//第一个参数是原空间的起始地址,第二个参数是新空间所占字节数
calloc将开辟空间初始化为0.
realloc开辟空间结果如下:
1>判断第一个参数是否为NULL。为空,操作等同于malloc。
2>第一个参数不为空。查看原空间的字节数与新开辟空间字节数的大小。
小于原空间字节数,返回当前空间的起始地址。
3>原空间字节数大于新开辟空间的字节数。检查该空间之后是否有足够的空间进行扩展
。有足够的空间扩展,直接进行扩展,返回原空间的起始地址。
4>不足扩展,开辟新空间、搬移元素、释放旧空间、返回新空间的起始地址。
注意:
malloc/calloc/realloc开辟的空间记得要free释放掉,否则会造成内存泄漏。
那么我们再考虑一个问题,编译器是怎么知道,要释放多大的空间的。我们猜想在开辟空间时是不是会额外开辟一定的空间来储存空间的一些信息。事实确实是这样。
在VS下,编译器会在真正能使用的空间之前开辟32字节的空间,储存空间的各种信息(开辟空间的大小、开辟空间时的文件名称、所在行等),在可用空间后还会开辟4字节的空间起到保护空间的作用。
int *p = (int *)malloc(sizeof(int));
开辟一个4字节空间,如图
new/delete
注意new、delete是一个操作符。
new底层调用的是operator new函数。
注意,这里的operator new函数并不是重载,要与operator=等区分开。
operator new原型如下:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)//直到开辟到空间退出循环
if (_callnewh(size) == 0)//当申请不成功时的应对措施,(释放对已经申请却不再使用的空间),当释放空间为0时,进行如下操作
{ // report no memory
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
new申请完空间之后,如果是自定义类,调用构造函数。
delete操作符类似,如果是自定义类,先调用析构函数,再调用operator delete函数
(实质上是free的封装)。
用new/delete来开辟/释放空间
int *p1 = new int;
int *p2 =new int(2);//可以初始化
int *p3 = new int[3];//可以一次创建多个元素
delete p1;
delete p2;
delete[] p3;
**注意:**delete[]与new[]匹配使用
new[]/delete[]####
new[]申请空间时过程:(显示析构函数时)
1>申请空间 调用operator new[](显示析构函数时开辟空间大小为sizeof(T)*N+4)
2>调用N次构造函数
3>将对象的个数存于前4个字节中
4>指针偏移4个字节,返回起始地址
delete[]申请空间时过程:(显示析构函数时)
1>从空间前四个字节取出析构函数调用次数
2>调用N次析构函数
3>释放空间