代码之家  ›  专栏  ›  技术社区  ›  Joshua Cheek

免费怎么知道免费多少钱?

  •  323
  • Joshua Cheek  · 技术社区  · 15 年前

    在C编程中,您可以将任何类型的指针作为参数传递给free,它如何知道分配给free的内存的大小?每当我传递一个指向某个函数的指针时,我也必须传递大小(即一个包含10个元素的数组需要接收10作为参数来知道数组的大小),但我不必将大小传递给自由函数。为什么不呢,我可以在自己的函数中使用相同的技术来避免我需要在数组长度的额外变量周围推车吗?

    10 回复  |  直到 8 年前
        1
  •  299
  •   inspector-g    11 年前

    当你打电话 malloc() 指定要分配的内存量。实际使用的内存量略大于此值,并包含记录(至少)块大小的额外信息。您不能(可靠地)访问其他信息—您也不应该:—)。

    当你打电话 free() 它只需查看额外的信息就可以知道这个块有多大。

        2
  •  120
  •   paxdiablo    9 年前

    大多数C内存分配函数的实现将为每个块存储记帐信息,可以是在线的,也可以是单独的。

    一种典型的方法(内联)是实际分配一个头部和所需的内存,将其填充到最小的大小。例如,如果您要求20个字节,系统可能会分配一个48字节的块:

    • 包含大小、特殊标记、校验和、指向下一个/上一个块的指针等的16字节头。
    • 32字节数据区(您的20字节被填充成16的倍数)。

    然后提供给您的地址是数据区域的地址。然后,当你放开这个街区的时候, free 只需记下你给它的地址,假设你还没有把地址或它周围的记忆填满,在它之前检查一下会计信息。从图形上看,这将沿着以下几条线:

     ____ The allocated block ____
    /                             \
    +--------+--------------------+
    | Header | Your data area ... |
    +--------+--------------------+
              ^
              |
              +-- The address you are given
    

    请记住,头和填充的大小完全是实现定义的(实际上,整个内容都是实现定义的) (a) 但在线会计选择是一种常见的选择)。

    会计信息中存在的校验和和特殊标记通常是导致错误的原因,如“内存领域损坏”或“双重释放”,如果您覆盖或释放它们两次。

    填充(为了使分配更有效)是为什么您有时可以在请求的空间末尾写一点,而不会造成问题(但是,不要这样做,这是未定义的行为,而且,仅仅因为它有时工作,并不意味着可以这样做)。


    (a) 我已经写了 malloc 在嵌入式系统中,无论您要求什么(这是系统中最大结构的大小),都会得到128个字节,假设您要求的是128个字节或更少(更多的请求将以空返回值满足)。使用一个非常简单的位掩码(即,不是内联的)来决定是否分配了一个128字节的块。

    我开发的其他程序有16字节块、64字节块、256字节块和1K块的不同池,同样使用位掩码来决定使用或可用的块。

    这两种选择都设法减少了会计信息的开销,并提高了 马洛克 自由的 (释放时不需要合并相邻块),在我们工作的环境中尤其重要。

        3
  •  45
  •   jdehaan    15 年前

    comp.lang.c 常见问题清单: How does free know how many bytes to free?

    malloc/free实现在分配时记住每个块的大小,因此在释放时不必提醒它大小。(通常,大小存储在分配的块附近,这就是为什么如果分配的块的边界甚至稍微超出了一点,事情通常会严重中断的原因)

        4
  •  6
  •   Community CDub    8 年前

    此答案从 How does free() know how much memory to deallocate? 我被一个明显重复的问题打断了回答。该答案应与本副本相关:


    对于这种情况 malloc 堆分配器将原始返回指针的映射存储到 free 稍后再调用内存。这通常涉及以与正在使用的分配器相关的任何形式存储内存区域的大小,例如原始大小、用于跟踪分配的二进制树中的节点或正在使用的内存“单位”计数。

    自由的 如果您“重命名”指针或以任何方式复制指针,则不会失败。但是,它不计算引用,而且只计算第一个 自由的 会是正确的。附加的 自由的 S是“双自由”错误。

    试图 自由的 任何值与上一个返回值不同的指针 马洛克 但到目前为止,释放是一个错误。无法部分释放从返回的内存区域 马洛克 .

        5
  •  4
  •   EFraim    15 年前

    在相关的笔记上 GLib 库具有不保存隐式大小的内存分配函数,然后您只需将大小参数传递给free即可。这可以消除部分开销。

        6
  •  3
  •   Community CDub    8 年前

    malloc() free() 是否依赖于系统/编译器,因此很难给出具体的答案。

    更多信息 on this other question .

        7
  •  2
  •   Timbo    15 年前

    当调用时,堆管理器将属于已分配块的内存量存储在某个位置 malloc .

    我自己从来没有实现过一个,但我猜在分配的块前面的内存可能包含元信息。

        8
  •  2
  •   DigitalRoss    12 年前

    最初的技术是分配一个稍大的块并在开始时存储大小,然后将博客的其余部分交给应用程序。额外的空间可以容纳一个大小和链接,将自由块连接在一起以便重用。

    但是,这些技巧存在一些问题,例如缓存和内存管理行为不佳。在块中正确地使用内存会导致不必要地分页,并且还会创建脏页,这会使共享和写时复制复杂化。

    所以更先进的技术是保持一个单独的目录。在内存区域使用两种大小相同的能力的情况下,也开发了一些奇特的方法。

    一般来说,答案是: 分配一个单独的数据结构来保持状态。

        9
  •  1
  •   MSalters    15 年前

    回答你问题的后半部分:是的,你可以,在C中一个相当常见的模式是:

    typedef struct {
        size_t numElements
        int elements[1]; /* but enough space malloced for numElements at runtime */
    } IntArray_t;
    
    #define SIZE 10
    IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
    myArray->numElements = SIZE;
    
        10
  •  0
  •   Varun Chhangani    12 年前

    当我们调用malloc时,它只需要根据需要消耗更多的字节。更多的字节消耗包含诸如校验和、大小和其他附加信息等信息。 当我们在那个时候调用free时,它直接转到附加信息,在那里可以找到地址,还可以找到有多少块是空闲的。