可以安全地假设 realloc()ing 到更小的大小总是会成功吗?

Is it safe to assume that realloc()ing to a smaller size will always succeed?

没有理由 realloc() 缩小到较小的尺寸会失败。它正在释放其余部分。我认为它没有任何失败的理由。话虽如此,假设 realloc() 缩小尺寸永远不会失败是否安全?

来自linux manpages

The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any built-in type and may be different from ptr. [...] If realloc() fails, the original block is left untouched; it is not freed or moved.

不能假定块不会被移动,因为这是特定于实现的。例如,在压缩的情况下可以移动块。

“没有理由重新分配到更小的尺寸会失败。”是没有证据的断言。

由于标准 C 库的规范不要求归约以永不失败,所以健壮的代码不会假定错误是不可能的,即使不太可能。


特别是,C17dr 规范有未来的库方向,其中讨论了减少到 0 的问题。

Invoking realloc with a size argument equal to zero is an obsolescent feature.

我认为这意味着现在和将来,应避免将分配减少为 0 的以下代码。

void *p = malloc(42);
...
realloc(p, 0);  // Obsolete
// and instead
free(p);

对于请求的大小小于原始大小且非零的情况,可以在调用 realloc 之前安全地复制原始指针,并将指针设置回该值,以防万一重新分配 returns null。如果 realloc 大小为零,那么事情就有点模糊了。某些实现会将 realloc(ptr, 0); 视为等同于 free(ptr); return 0;,这将在释放对象后 return null,但其他实现会将其视为等同于 realloc(ptr,1);,这只会 return null 在原始指针仍然有效的情况下。不幸的是,没有通用的方法知道实现将使用哪种行为,因此无法正确处理来自 realloc(ptr, 0);.

的空值 return

TL;DR

不,你不能这样假设。

There is no reason for realloc()ing to a smaller size will fail. It's freeing up the remainder. I see no reason at all for it to fail.

chux 详细介绍了这一点。所以我以更笼统的方式回答。

您在这里使用的推理类型非常危险。你的基本推理是 “我不明白为什么 X 是真的,因此我假设 X 是假的。” 以这种方式推理时要非常小心。

首先,让我们跳过一个非常明显的危险,即使您看不出 realloc 在这种情况下失败的任何原因,但这并不意味着您是正确的。

相反,让我们假设您是正确的。可以证明,没有任何合理的理由以新大小等于或小于原始大小的方式实施 realloc ,以至于它永远不会失败。那么它仍然是一个错误的论点,因为你不能假设编写你正在使用的实现代码的程序员有这方面的知识。有一种可证明的最佳方法可以做到这一点,但编码人员并不知道。

另外,C 标准没有说这是安全的这一事实很好地表明(但是,不是证明)有充分的理由不提供这种保证。

如果规范没有说它在某些情况下总是成功,那么您应该始终将失败的风险视为非零。在这种情况下,标准没有给出任何承诺,所以不,你不能假设。

另外,现实中也常有这样的情况,用“好”的方式实现起来相对容易,但还是会比最简单的方式复杂。有时这种简单性是可取的。我能想到的实现 realloc 的最简单方法是这样的:

void *realloc(void *ptr, size_t new_size)
{
    void *ret = malloc(new_size);
    if(ret) {
        memcpy(ret, ptr, new_size);
        free(ptr);
    }
    return ret;
}

以这种方式实现它的一个非常有效的原因是您有一个特定的环境,在该环境中您通常永远不会使用 realloc 并且您为了符合标准而将其投入使用。每当你做某事的唯一目的是符合标准或规范时,你通常会把简单放在首位。

There is no reason for realloc()ing to a smaller size will fail.

考虑从平台的底层地址 space 分配器中获取大块并将它们切成小块的实现。如果请求的大小不在重新分配的块来自的大块的支持大小范围内,则减少分配大小的 realloc 可能需要分配新块。

在这种情况下,实现将需要从子分配器获取较小的块,该子分配器的服务大小范围包括请求的大小。该子分配器可能没有任何空闲块,并且当它请求一个新的大块来分割时,可能会失败。

所以本题前提不成立

另外,总的来说,从“我想不出这会失败的任何原因”跳到“我可以假设这不会失败”是一个糟糕的想法。由于人们无法预见的原因而失败的事情有很多,其中一些后果很可怕。