7.8 存储器分配
7.8 存储器分配
ANSI C说明了三个用于存储空间动态分配的函数。
(1) malloc。分配指定字节数的存储区。此存储区中的初始值不确定。
(2) calloc。为指定长度的对象,分配能容纳其指定个数的存储空间。该空间中的每一位( b i t )都初始化为0。
(3) realloc。更改以前分配区的长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,以便在尾端提供增加的存储区,而新增区域内的初始值则不确定。
#include <stdlib.h>
void *malloc(size_t size) ;
void *calloc(size_t n o b j, size_t size) ;
void *realloc(void *ptr, size_t newsize) ;
//三个函数返回:若成功则为非空指针,若出错则为NULL
void free(void *ptr)
这三个分配函数所返回的指针一定是适当对齐的,使其可用于任何数据对象。例如,在一个特定的系统上,如果最苛刻的对齐要求是double,则对齐必须在8的倍数的地址单元处,那么这三个函数返回的指针都应这样对齐。
因为这三个a l l o c函数都返回通用指针指针void *,所以如果在程序中包括了< s t d l i b . h > (包含了函数原型),那么当我们将这些函数返回的指针赋与一个不同类型的指针时,不需要作类型强制转换。
函数free释放ptr指向的存储空间。被释放的空间通常被送入可用存储区池,以后可在调用分配函数时再分配。
realloc使我们可以增、减以前分配区的长度(最常见的用法是增加该区)。例如,如果先分配一个可容纳长度为5 1 2的数组的空间,并在运行时填充它,但又发现空间不够,则可调用realloc扩充该存储空间。如果在该存储区后有足够的空间可供扩充,则可在原存储区位置上向高地址方向扩充,并返回传送给它的同样的指针值。如果在原存储区后没有足够的空间,则realloc分配另一个足够大的存储区,将现存的5 1 2个元素数组的内容复制到新分配的存储区。因为这种存储区可能会移动位置,所以不应当使用任何指针指在该区中。习题4 . 1 8显示了在getcwd中如何使用realloc,以处理任何长度的路径名。程序17-28是使用realloc的另一个例子,用其可以避免使用编译时固定长度的数组。
注意,realloc的最后一个参数是存储区的newsize(新长度),不是新、旧长度之差。作为一个特例,若ptr是一个空指针,则realloc的功能与malloc相同,用于分配一个指定长度newsize的存储区。
此功能是由ANSI C新引进的。如果传送一个NULL指针,realloc的早期版本会失败。早期版本允许realloc自上次malloc, realloc或c a l l o c以来所释放的块。这种技巧可回溯到V 7,它利用malloc实现存储器紧缩的搜索策略。4 . 3 + BSD仍支持这一功能,而SVR4则不支持。这种功能不应再使用。
这些分配例程通常通过s b r k ( 2 )系统调用实现。该系统调用扩充(或缩小)进程的堆(见图7 - 3 )。malloc和free的一个样本实现请见K e r n i g h a n和Ritchie [1988] 的8 . 7节。
虽然s b r k可以扩充或缩小一个进程的存储空间,但是大多数malloc和free的实现都不减小进程的存储空间。释放的空间可供以后再分配,但将它们保持在malloc池中而不返回给内核。
应当注意的是,大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针等等。这就意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的,但是因为这种错误不会很快就暴露出来,所以也就很难发现。将指向分配块的指针向后移动也可能会改写本块的管理信息。
在动态分配的缓冲区前或后进行写操作,破坏的可能不仅仅是该区的管理记录信息,在动态分配的缓冲区前后的存储区很可能用于其他动态分配的对象。这些对象与破坏他们的代码可能无关,这造成寻求信息破坏的源头更加困难。
其他可能产生的致命性的错误是:释放一个已经释放了的块;调用free时所用的指针不是
三个a l l o c函数的返回值等。如若一个进程调用malloc函数,但却忘记调用free函数,那么该进程占用的存储器就会连续增加,这被称为泄露leakage。不调用free函数以释放不使用的空间,那么进程地址空间长度就会慢慢增加,直至不再有空闲空间,此时,由于过度的分页开销,因而使得性能下降。
因为存储器分配出错很难跟踪,所以某些系统提供了这些函数的另一种实现方法。每次调用这三个分配函数中的任意一个或free时都进行附加的出错检验。在调用连接编辑程序时指定一个专用库,则在程序中就可使用这种版本的函数。此外还有公共可用的资源(例如由4 . 3 + BSD所提供的),在对其进行编译时使用一个特殊标志就会使附加的运行时间检查生效。
因为存储空间分配程序的操作对某些应用程序的运行时间性能非常重要,所以某些系统供了附加能力。例如, SVR4提供了名为m a l l o p t的函数,它使进程可以设置一些变量,并用它们来控制存储空间分配程序的操作。还可使用另一个名为m a l l i n f o的函数,以对存储空间分配程序的操作进行统计。请查看所使用系统的malloc ( 3 )手册页,弄清楚这些功能是否可用。
可以替代存储器分配程序的函数和库。
1. libmalloc
2. vmalloc
3. 快速分配quick-fit
4. alloca函数。
可以查查相关的信息。