为什么 `realloc` 不在可能的情况下就地重新分配?

Why does `realloc` not re-allocate in-place when possible?

来自 c99:

The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size. The contents of the new object shall be the same as that of the old object prior to deallocation, up to the lesser of the new and old sizes. Any bytes in the new object beyond the size of the old object have indeterminate values.

[..]

The realloc function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object could not be allocated.

我很惊讶标准没有指定 realloc 应该“尝试”进行就地重新分配。通常,如果重新分配的大小小于当前分配的大小,我会期望标准确保 realloc 将 return 相同的指针。

标准是否有逻辑不指定 realloc 如果尺寸减小则应该就地?

我想我会提出这个答案。

"try" 的概念在标准的上下文中没有多大意义 - 实施者在尝试足够多之前必须尝试多努力?如何衡量合规性?

许多常见的实现将完全按照您的建议工作:如果您正在向下调整大小,甚至向上调整大小并且以下内存恰好是空闲的,它们可能 return 调整内务处理后的原始指针但是无需复制任何数据。耶!

但我能想到很多分配器即使可能也不会这样做的原因:

  • 一些分配器为不同的大小保留不同的区域,其中(弥补)它是一个不同的池,用于 1-128 字节的块与 64kbytes 和更大的块。如果 "big" 池必须保持小分配,整个方案就会崩溃。如果您有意在页面边界上保留 "big" 分配,则尤其如此。

  • 多线程感知应用程序通常必须特别注意避免争用,这样内存分配就不会成为瓶颈。如果你正在重新分配一个在不同线程中分配的块,那么给你一个新块(带副本)并推迟释放旧指针可能是非阻塞的,但允许你保留相同的指针会阻止这个或其他一些线程。

  • 调试分配器会有意return不同的指针以确保程序不会错误地挂在旧指针上:这越早越好。

我想不出标准中的 "please try" 语句会改变任何库设计者的任何决定的情况。如果保持相同的指针对于给定的实现有意义,那么 当然 他们会使用它,但如果有一个压倒一切的技术原因不这样做,那么他们就不会。

我也不确定我是否能想到这种推动会对图书馆的 user 产生任何影响的情况。您仍然需要对其进行编码以说明所有情况,即使是 "tries" 的情况,因此它不会为您节省任何代码。

最后,这是一个标准永远不会给实施者戴上手铐的实施细节,库将根据其自身的优点(性能、代码大小、可靠性等)进行判断,这只是一个方面.

如果出于某种原因确实需要这种行为,您始终可以编写自己的分配器。


编辑:分配器想要return不同指针的另一个原因,即使重新分配相同大小:减少内存碎片。

如果我的 realloc 请求在两边都有很多空闲 space 的时候出现,分配器可能会意识到:我可以就地扩展这个块(快速而简单),或者我可以把它移到其他地方,然后把剩下的东西合并成一个更大的空闲块。

这一直是一个客户写的项目的老毛病:很久以前用32位写的Delphi,一次运行好几天,内存压力很大,最后内存不足如此分散以至于即使有数百兆字节的空闲空间也无法满足适度的请求。

参考:

在 Delphi 中我对此无能为力,但在 C 中很容易想象 "aggressive avoiding of memory fragmentation" 是分配器的 属性。

标准是通用的描述,如果尝试做某事,则不需要指定,它描述了函数的一般行为。

很明显,出于优化目的,系统将尝试在同一位置调整缓冲区大小,但由于无法保证会发生这种情况, 标准里没法规定.