首页 文章

免费如何知道免费多少?

提问于
浏览
312

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

10 回答

  • 292

    来自 comp.lang.c 常见问题列表:How does free know how many bytes to free?

    malloc / free实现会记住每个块的大小,因此在释放时不必提醒它的大小 . (通常,大小存储在分配的块附近,这就是为什么如果分配的块的边界甚至略微超越,事情通常会严重破坏)

  • 6

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

    当你调用 free() 时,它只是查看额外的信息以找出块的大小 .

  • 3

    大多数C存储器分配函数的实现将存储每个块的计费信息,可以是内联的,也可以是单独的 .

    一种典型的方式(内联)是实际分配 Headers 和你要求的内存,填充到一些最小尺寸 . 例如,如果您要求20个字节,系统可能会分配一个48字节的块:

    • 包含大小,特殊标记,校验和,指向下一个/上一个块的指针等16字节标头 .

    • 32字节数据区(您的20个字节填充为16的倍数) .

    然后给你的地址是数据区的地址 . 然后,当您释放该块时, free 将只是获取您提供的地址,并且假设您没有填写该地址或其周围的内存,请检查它之前的会计信息 . 从图形上看,这将是:

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

    请记住 Headers 的大小和填充是完全实现定义的(实际上,整个过程是实现定义的(a),但是内联记帐选项是常见的) .

    会计信息中存在的校验和和特殊标记通常是错误的原因,如“内存竞技场损坏”或“双重免费”,如果您覆盖它们或释放它们两次 .

    填充(为了提高分配效率)是为什么你有时可以在你所请求空间的末尾写一点而不会引起问题(仍然,不要这样做,它是未定义的行为,只是因为它有时有效,不会这意味着可以这样做) .


    (a)我在嵌入式系统中编写了 malloc 的实现,无论你要求什么(这是系统中最大结构的大小),你都得到128个字节,假设你要求128个字节或更少(请求更多)会遇到NULL返回值) . 使用非常简单的位掩码(即,不是串联的)来确定是否分配了128字节的块 .

    我开发的其他人有16个字节块,64个字节块,256个字节块和1K块的不同池,再次使用位掩码来决定使用或可用的块 .

    这两个选项都设法减少了会计信息的开销,并提高了 mallocfree 的速度(在释放时不需要合并相邻的块),这在我们工作的环境中尤其重要 .

  • 1

    这个答案从How does free() know how much memory to deallocate?重新定位,在那里,我被一个明显重复的问题无法回答 . 那么这个答案应该与这个副本相关:


    对于 malloc 的情况,堆分配器将原始返回指针的映射存储到稍后对内存所需的相关详细信息 . 这通常涉及以与使用中的分配器相关的任何形式存储存储器区域的大小,例如原始大小,或用于跟踪分配的二叉树中的节点,或者使用中的存储器计数"units" .

    free 如果指针"rename"将不会失败,或以任何方式复制它 . 然而,它不是引用计数,只有第一个 free 才是正确的 . 额外的 free 是"double free"错误 .

    尝试 free 任何指针的值与先前 malloc s返回的值不同,但尚未实现是错误 . 无法部分释放 malloc 返回的内存区域 .

  • 117

    在相关的注释GLib库具有内存分配函数,它们不保存隐式大小 - 然后您只需将size参数传递给free . 这可以消除部分开销 .

  • 1

    malloc()free() 取决于系统/编译器所以很难给出具体的答案 .

    更多信息on this other question .

  • 44

    当您调用 malloc 时,堆管理器会在某处存储属于已分配块的内存量 .

    我自己从来没有实现过,但我想分配块前面的内存可能包含元信息 .

  • 2

    回答问题的后半部分:是的,你可以,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;
    
  • 0

    最初的技术是分配一个稍大的块并在开头存储大小,然后将应用程序提供给博客的其余部分 . 额外的空间包含一个大小,并且可能链接到将空闲块连接在一起以便重用 .

    但是,这些技巧存在某些问题,例如缓存和内存管理行为不佳 . 在块中使用内存往往会不必要地对页面进行分页,并且还会创建脏页面,这使得共享和写入时复制变得复杂 .

    所以更先进的技术是保持一个单独的目录 . 还开发了外来方法,其中存储器区域使用相同的两个功率大小 .

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

  • 4

    当我们调用malloc时,它只是从它的要求中消耗更多的字节 . 这种更多的字节消耗包含校验和,大小和其他附加信息等信息 . 当我们在那时免费拨打电话时,它会直接查找其中找到地址的附加信息,并查找有多少块可以免费使用 .

相关问题