进程环境之存储器分配

ISO C说明了三个用于存储器空间动态分配的函数:

(1)malloc。分配指定字节数的存储区。此存储区中的初始值不确定。

(2)calloc。为指定数量具有指定长度的对象分配存储空间。该空间中的每一位都初始化为0。

(3)realloc。更改以前分配区的长度(增加或减少)。当增加长度时,可能需要将以前分配区的内容移到另一个足够大的区域,以便在尾端提供增加的存储区(此时,返回新分配区的指针,否则返回原来的指针值),而新增区域内的初始值则不确定。

#include <stdlib.h>

void *malloc( size_t size );
void *calloc( size_t nobj, size_t size );
void *realloc( void *ptr, size_t newsize );
三个函数返回值:若成功则返回非空指针,若出错则返回NULL

void free( void *ptr );

这三个分配函数所返回的指针一定是适当对齐的,使其可用于任何数据对象。

因为这三个alloc函数都返回通用指针void *,所以如果在程序中包括了#include <stdlib.h>(以获得函数原型),那么当我们将这些函数返回的指针赋予一个不同类型的指针时,就不需要显式地执行类型强制转换。

函数free释放ptr指向的存储空间。被释放的空间通常被送入可用存储区池,以后,可在调用上述三个分配函数时再分配。

realloc函数使我们可以增、减以前分配区的长度(最常见的用法是增加该区)。增加该区时,如果在该存储区后有足够的空间可供扩充,则可在原存储区位置上向高地址方向扩充,无需移动任何原先的内容,并返回传送给它的同样的指针值。如果在原存储区后没有足够的空间,则realloc分配另一个足够大的存储区,将原存储区中的内容复制到新分配的存储区。然后,释放原存储区,返回新分配区的指针。因为这种存储区可能会移动位置,所以不应当使任何指针指到该区中。

注意,realloc的最后一个参数是存储区的newsize(新长度),它不是新、旧存储区长度之差。作为一个特例,若ptr是一个空指针,则realloc的功能与malloc相同,用于分配一个指定长度为newsize的存储区。

这些分配例程通常用sbrk(2)系统调用实现。该系统调用扩充(或缩小)进程的堆。

虽然sbrk可以扩充或缩小进程的存储空间,但是大多数malloc和free的实现都不减少进程的存储空间。释放的空间可供以后再分配,但通常将它们保持在malloc池中而不返回给内核。

应当注意的是,大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度、指向下一个分配块的指针等等。这就意味着如果超过一个已分配区的尾端进行写操作,则会重写后一个块的管理记录。这种类型的错误时灾难性的,但是因为这种错误不会很快暴露出来,所以也就很难发现。同样,在已分配区起始位置之前进行写操作会重写本块的管理记录。

在动态分配的缓冲区前或后进行写操作,破坏的可能不仅仅是该区的管理记录信息。在动态分配的缓冲区前后的存储区很可能用于其他动态分配的对象。这些对象与破坏它们的代码可能无关,这造成寻求信息破坏的源头更加困难。

其他可能产生的致命性错误是:释放一个已经释放了的块;调用free时所用的指针不是三个alloc函数的返回值等。如若一个进程调用malloc函数,但却忘记调用free函数,那么该进程占用的存储器就会连续增加,这被称为泄漏(leakage)。不调用free函数以释放不再使用的空间,那么进程地址空间长度就会慢慢增加,直至不再有空闲空间。此时,由于过度的分页开销,因而使性能下降。

因为存储器分配出错很难跟踪,所以某些系统提供了这些函数的另一种实现版本。每次调用这三个分配函数中的任意一个或free时,它们都进行附加的检错。在调用连接编辑器时指定一个专用库,则在程序中就可使用这种版本的函数。此外还有公共可用的资源,在对其进行编译时使用一个特殊标志就会使附加的运行时检查生效。

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

原文地址:https://www.cnblogs.com/nufangrensheng/p/3508273.html