免费如何知道免费多少?

时间:2009-10-05 07:45:32

标签: c size pointers free

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

11 个答案:

答案 0 :(得分:310)

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

当您致电free()时,它只会查看额外信息,以了解该块的大小。

答案 1 :(得分:124)

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字节或更少(请求更多将满足NULL返回值)。一个非常简单的位掩码(即不是内联的)用于决定是否分配了一个128字节的块。

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

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

答案 2 :(得分:46)

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

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

答案 3 :(得分:6)

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


对于malloc的情况,堆分配器将原始返回指针的映射存储到稍后free内存所需的相关详细信息。这通常涉及以与使用中的分配器相关的任何形式存储存储器区域的大小,例如原始大小,或用于跟踪分配的二叉树中的节点,或者使用中的存储器“单元”的计数。 p> 如果您“重命名”指针或以任何方式复制指针,

free都不会失败。然而,它不是引用计数,只有第一个free才是正确的。额外的free是“双重免费”错误。

尝试free任何指针的值与前一个malloc返回的指针不同,但尚未确认是错误。无法部分释放从malloc返回的内存区域。

答案 4 :(得分:4)

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

答案 5 :(得分:3)

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

更多信息on this other question

答案 6 :(得分:2)

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

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

答案 7 :(得分:2)

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

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

因此,更高级的技术是保留一个单独的目录。还开发了外来方法,其中存储区域使用相同的两种功率。

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

答案 8 :(得分:1)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

要回答第二个问题,是的,您可以(使用)与malloc()相同的技术 只需将每个数组内的第一个单元格分配给数组的大小即可。 这样您就可以发送数组而无需发送额外的size参数。