免费如何知道要多少钱?


384

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


一个类似的问题:stackoverflow.com/questions/851958/…(尽管我说不是很重复)
约翰·卡特

好友系统是另一种方式来做到这一点,可以计算出基于指针,而无需在每个块的开销。
EvilTeach 2014年

Answers:


348

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

当您致电时free(),它仅查看额外信息以找出障碍物的大小。


44
仅供参考,例如BSD必须malloc_size()malloc()ed指针可靠地访问块大小。但是没有可靠,可移植的方法。
laalto

50
我认为重要的一点是,这个额外的信息块位于返回的指针之前。
乔治·Schölly

39
@gs这取决于实现。但是,是的,通常就是这样。
法莱纳

31
如果可以free()要求程序员准确地报告malloc()块有多大,您能想象到恐怖吗?内存泄漏本身已经很严重了。
MusiGenesis'4

35
为什么这些信息可供malloc()和使用free(),但是您必须存储数组的大小?blockSize(ptr)如果他们仍然存储信息,他们为什么不可以做类似的事情?
corsiKa 2014年

144

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字节块和1K块具有不同的池,再次使用位掩码来确定使用或可用的块。

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


@paxdiablo这是否意味着malloc不分配连续的内存块?
user10678

2
@ user10678的唯一真正要求malloc是,在成功的情况下,它为您提供至少与您要求的内存一样大的内存块。就如何访问其中的各个元素而言,各个块是连续的,但不要求这些块来自的领域是连续的。
paxdiablo

相关问题:为什么malloc / free没有变化,您在释放时在其中指定大小,因此不必存储大小?
user253751 '19

@ user253751,因为那时theer的一个需要的东西跟踪,在和指针本身上面。这既是不必要的危险的:void *x = malloc(200); free(x, 500);没有好下场的:-)在任何情况下,为了提高效率,实际的缓冲区的大小可能会更大(你就不能依靠这一点)。
paxdiablo

@paxdiablo它还可以避免浪费内存来保持大小。
user253751 '19

47

comp.lang.cFAQ列表中:free如何知道要释放多少字节?

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


2
这不是一个答案。问题是这样的:为什么可以自由地可靠地查找块的大小,但是程序员没有可用的功能呢?
Bananach

这确实是malloc api的实现细节,并且没有api以标准方式(据我所知)获取此信息。“系统”记录它并在上使用它free。也许答案并不令您满意,但我认为您不会获得具有更通用的信息的方法:-)
jdehaan

6

这个答案是从释放的。free()如何知道要释放多少内存?一个明显的重复问题使我无法回答。然后,此答案应与此重复项有关:


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

free如果您“重命名”指针或以任何方式复制指针,都不会失败。但是,它不算作参考,只有第一个free是正确的。其他frees是“双重释放”错误。

尝试free使用与先前mallocs 返回的值不同且尚未释放的值的任何指针都是错误。无法部分释放从返回的内存区域malloc


我更改了malloc调用返回的指针的值。而且我没有错误地释放了它。为什么?在这里看到:stackoverflow.com/questions/42618390/...
smwikipedia

4

值得一提的是,GLib库具有不分配隐式大小的内存分配功能-然后您只需将size参数传递给free。这样可以消除部分开销。


3

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

有关此其他问题的更多信息。


2
它们实际上是依赖于库的(通常是C库,通常与OS紧密联系)。对于编译器来说,它们只是函数。
Donal Fellows 2010年

2

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

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


3
那是一种可能的实现,但是可以设计一种系统,在该系统中,可以在完全不同的页面中的单个表中跟踪所有内存,而不必在分配内存池附近的任何位置进行。
迅速

2

最初的技术是分配一个稍大的块并在开始时存储大小,然后将其余的博客提供给应用程序。多余的空间将保持一定的大小,并可能具有链接,以将空闲的块连接在一起以供重用。

但是,这些技巧存在某些问题,例如不良的缓存和内存管理行为。在块中正确使用内存往往会不必要地分页存储,还会创建脏页,使共享和写时复制变得复杂。

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

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


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;

这与BSD malloc用于小对象的技术完全不同(尽管这是创建Pascal样式数组的完美技术)
Pete Kirkham 2009年

0

当我们调用malloc时,它只是消耗了更多的字节数。这更多的字节消耗包含诸如校验和,大小和其他附加信息之类的信息。当我们在那个时候打电话给free时,它直接转到该附加信息,在该地址中找到地址并找到多少空闲块。


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.