自由的人怎么知道该自由多少?

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

88351 次浏览

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

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

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

当你调用free()时,它只是查看额外的信息,以确定块有多大。

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

更多信息关于另一个问题

comp.lang.c常见问题列表

malloc/free实现在分配每个块时记住它的大小,因此在释放时不需要提醒它大小。(通常,大小存储在已分配块的相邻位置,这就是为什么如果已分配块的边界稍有超出,情况通常会很糟糕)

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

口齿伶俐的库具有内存分配函数,不保存隐式大小-然后您只需将size参数传递给free。这可以减少部分开销。

这个答案是从free()如何知道释放多少内存?重新安置的,在那里我突然被一个明显重复的问题阻止回答。这个答案应该与这个副本相关:


对于malloc的情况,堆分配器存储了原始返回指针的映射,映射到稍后freeing内存所需的相关细节。这通常涉及以与正在使用的分配器相关的任何形式存储内存区域的大小,例如原始大小,或用于跟踪分配的二叉树中的节点,或正在使用的内存“单元”的计数。

如果你“重命名”指针,或以任何方式复制它,free将不会失败。然而,它不被计数,只有第一个free是正确的。额外的__abc0是“double free”错误。

试图free任何指针的值与以前的__abc1返回的值不同,并且尚未释放,将会出错。不可能部分释放从malloc返回的内存区域。

大多数C内存分配函数的实现都将为每个块存储记帐信息,或者内联存储,或者单独存储。

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

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

然后给您的地址就是数据区域的地址。然后,当你释放块时,free将简单地获取你给它的地址,并且假设你没有填充该地址或它周围的内存,在它之前立即检查会计信息。从图形上看,这将沿着以下路线:

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

请记住头文件的大小和填充完全是实现定义的(实际上,整个内容是实现定义的(一),但内嵌记帐选项是一个常见的选项)。

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

填充(使分配更有效)是为什么有时你可以在请求空间的末尾之外写一些而不会引起问题(仍然,不要这样做,这是未定义的行为,只是因为它有时有效,并不意味着可以这样做)。


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

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

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

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

然而,这些技巧也存在一些问题,比如糟糕的缓存和内存管理行为。在块中使用内存会导致不必要的分页,还会创建脏页,使共享和写时复制变得复杂。

因此,一个更高级的技术是保持一个单独的目录。还开发了一些奇异的方法,其中内存区域使用相同的2次方大小。

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

当我们调用malloc时,它只是从它的需求中消耗更多的字节。这更多的字节消耗包含信息,如校验和,大小和其他额外的信息。 当我们在那个时候调用free时,它会直接找到附加信息,在那里它会找到地址,也会找到有多少块是空闲的

来回答第二个问题,是的,你可以(某种程度上)使用与malloc()相同的技术 通过简单地将每个数组中的第一个单元格分配给数组的大小。 这样就可以在不发送额外size参数的情况下发送数组