美文网首页深入理解计算机系统
[c/c++]2.malloc、calloc、realloc

[c/c++]2.malloc、calloc、realloc

作者: MachinePlay | 来源:发表于2020-02-15 10:26 被阅读0次

    储存空间动态分配

    image.png

    ISO C说明了3个用于存储空间动态分配的函数。

    • (1)malloc,分配指定字节数的存储区。此存储区中的初始值不确 定。
    • (2)calloc,为指定数量指定长度的对象分配存储空间。该空间中 的每一位(bit)都初始化为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);
    

    3个函数返回值:若成功,返回非空指针;若出错,返回NULL

    void free(void *ptr);
    

    这3个分配函数所返回的指针一定是适当对齐的,使其可用于任何 数据对象。例如,在一个特定的系统上,如果最苛刻的对齐要求是, double必须在8的倍数地址单元处开始,那么这3个函数返回的指针都应 这样对齐。

    因为这 3 个 alloc 函数都返回通用指针 void *,所以如果在程序中包 括了#include<stdlib.h>(以获得函数原型),那么当我们将这些函数返 回的指针赋予一个不同类型的指针时,就不需要显式地执行强制类型转换。未声明函数的默认返回值为int,所以使用没有正确函数声明的强制 类型转换可能会隐藏系统错误,因为int类型的长度与函数返回类型值的 长度不同(本例中是指针)。

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

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

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

    这些函数的早期版本允许调用realloc分配自上次malloc、realloc 或calloc调用以来所释放的块。这种技巧可回溯到 V7,它利用 malloc 的搜索策略,实现存储器紧缩。Solaris仍支持这一功能,而很多其他 平台则不支持。这种功能不被赞同,不应再使用。

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

    虽然sbrk可以扩充或缩小进程的存储空间,但是大多数malloc和free 的实现都不减小进程的存储空间。释放的空间可供以后再分配,但将它 们保持在malloc池中而不返回给内核。
    大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间 用来记录管理信息——分配块的长度、指向下一个分配块的指针等。这 就意味着,如果超过一个已分配区的尾端或者在已分配区起始位置之前 进行写操作,则会改写另一块的管理记录信息。这种类型的错误是灾难 性的,但是因为这种错误不会很快就暴露出来,所以也就很难发现。
    在动态分配的缓冲区前或后进行写操作,破坏的可能不仅仅是该区 的管理记录信息。在动态分配的缓冲区前后的存储空间很可能用于其他 动态分配的对象。这些对象与破坏它们的代码可能无关,这造成寻求信 息破坏的源头更加困难。
    其他可能产生的致命性的错误是:释放一个已经释放了的块;调用 free时所用的指针不是3个alloc函数的返回值等。如若一个进程调用 malloc函数,但却忘记调用free函数,那么该进程占用的存储空间就会连 续增加,这被称为泄漏(leakage)。如果不调用free函数释放不再使用 的空间,那么进程地址空间长度就会慢慢增加,直至不再有空闲空间。 此时,由于过度的换页开销,会造成性能下降。
    因为存储空间分配出错很难跟踪,所以某些系统提供了这些函数的 另一种实现版本。每次调用这3个分配函数中的任意一个或free时,它们 都进行附加的检错。在调用连接编辑器时指定一个专用库,在程序中就 可使用这种版本的函数。此外还有公共可用的资源,在对其进行编译时 使用一个特殊标志就会使附加的运行时检查生效。

    有很多可替代malloc和free的函数。某些系统已经提供替代存储空间 分配函数的库。另一些系统只提供标准的存储空间分配程序。如果需 要,软件开发者可以下载替代函数。下面讨论某些替代函数和库。

    • 1.libmalloc
      基于SVR4的UNIX系统,如Solaries,包含了libmalloc库,它提供了 一套与ISO C存储空间分配函数相匹配的接口。libmalloc库包括mallopt函 数,它使进程可以设置一些变量,并用它们来控制存储空间分配程序的 操作。还可使用另一个名为mallinfo的函数,以对存储空间分配程序的 操作进行统计。

    • 2.vmalloc
      Vo[1996]说明一种存储空间分配程序,它允许进程对于不同的存储 区使用不同的技术。除了一些vmalloc特有的函数外,该库也提供了ISO C存储空间分配函数的仿真器。

    • 3.quick-fit
      历史上所使用的标准 malloc 算法是最佳适配或首次适配存储分配策 略。quick-fit(快速适配)算法比上述两种算法快,但可能使用较多存 储空间。Weinstock和Wulf[1988]对该算法进行了描述,该算法基于将存 储空间分裂成各种长度的缓冲区,并将未使用的缓冲区按其长度组成不 同的空闲区列表。现在许多分配程序都基于快速适配。

    • 4.jemalloc
      jemalloc函数实现是FreeBSD 8.0中的默认存储空间分配程序,它是 库函数malloc族在FreeBSD中的实现。它的设计具有良好的可扩展性,可 用于多处理器系统中使用多线程的应用程序。Evans[2006]说明了具体实 现及其性能评估。

    • 5.TCMalloc
      TCMalloc函数用于替代malloc函数族以提供高性能、高扩展性和高 存储效率。从高速缓存中分配缓冲区以及释放缓冲区到高速缓存中时,
      它使用线程-本地高速缓存来避免锁开销。它还有内置的堆检查程序和 堆分析程序帮助调试和分析动态存储的使用。TCMalloc库是开源可用 的,是Google-perftools工具中的一个。Ghemawat和Menage[2005]对此做 了简单介绍。

    • 6.函数alloca
      还有一个函数也值得一提,这就是alloca。它的调用序列与malloc相 同,但是它是在当前函数的栈帧上分配存储空间,而不是在堆中。其优 点是:当函数返回时,自动释放它所使用的栈帧,所以不必再为释放空 间而费心。其缺点是:alloca 函数增加了栈帧的长度,而某些系统在函 数已被调用后不能增加栈帧长度,于是也就不能支持alloca函数。尽管如 此,很多软件包还是使用alloca函数,也有很多系统实现了该函数。
      本书中讨论的4个平台都提供了alloca函数。

    相关文章

      网友评论

        本文标题:[c/c++]2.malloc、calloc、realloc

        本文链接:https://www.haomeiwen.com/subject/epkefhtx.html