在学习C/C++的过程中,malloc、realloc和free这些函数,都是内存操作方面最基本的函数,使用非常广泛。今天本文主要是浅显地介绍一点这些函数的原理。
Realloc
void *realloc(void *__ptr, size_t __size)
Realloc函数接受2个参数:一个指针和一个size,根据参数不同,做的事情也不同:
1、当ptr是一个空指针时,方法近似于malloc(不推荐使用);
3、当ptr非空,而size不为0时,方法是重新为ptr分配新的内存区域,尺寸为size。
一般来说,使用realloc时,比较容易出现问题的是这么些场景:
在这里,很多中文网络上的文章有一个误区是,认为realloc的size参数为0时函数的表现完全等于free。事实上,这是不确切的,而是和操作系统的malloc库实现有关。举个例子:
先调用realloc(a,0)再调用realloc(a,5)程序表现完全正常,不会崩溃,只是地址会重新分配,然而如果先调用free再调用realloc则会崩溃。
2、没有考虑realloc失败,返回值为NULL的情况,直接赋值导致指针指向了NULL,而之前的内存成为无法回收的野指针。
Malloc和free这对好基友才是本文的重点。在之前的文章中我们说过,内存操作的效率远低于纯算术运算。今天本文粗浅地介绍一些malloc和free的实现原理,就能理解其中的原因。
那么,在调用malloc时,系统库到底做了什么事情呢?
Malloc对空闲内存的管理,通过空闲链表来实现。最基础的空闲链表每一个节点包含两个字段:下一段空闲内存的地址和当前空闲段的大小。对上图的情况,对应的空闲链表的基本内容是这个样子的:
在执行free时,系统会将要free的这块区域标记为空闲可用,但并不会真正清除其中的值。也就是说free了一个指针然后马上去取之前地址中的值,这个值还存在。如下例所示:
对C/C++来说,这块地址标记为空闲后,下次再有代码申请到这一块堆内存并写入新值后,才会真正把原值覆盖掉。当然,在实际应用中并不推荐这样的代码。
同时,free还会做的一件事情是更新空闲链表,在更新时,如果检测到这段内存前后有连续空闲内存的话,还会将这几个相邻地址的链表节点进行合并,以提高遍历效率。
