【问题标题】:Is it safe to assume that realloc()ing to a smaller size will always succeed? [duplicate]假设 realloc() 到较小的大小总是会成功,这是否安全? [复制]
【发布时间】:2020-10-28 06:35:24
【问题描述】:

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

【问题讨论】:

  • 不要假设任何事情。始终检查错误。
  • C 标准不要求 realloc 在新大小更小的情况下成功。我们可以想象一个为调试目的而构建的内存分配库,它故意为每次分配提供新内存,并在每个释放的空间上写入指示数据,以便更容易检测到释放空间的错误使用。在这样的库中,将realloc 转换为较小的大小可能会失败,因为没有可提供的新内存。因此,C 标准允许这样做,并且有充分的理由发生这种情况。
  • 一旦你开始假设某事不会失败,你就会认为当你看不到失败的原因时你不需要检查事情。使用 C 的良好做法是进行这些检查。

标签: c realloc


【解决方案1】:

来自linux manpages

realloc() 函数返回一个指向新分配内存的指针,该指针适合任何内置类型对齐,并且可能与 ptr 不同。 [...] 如果realloc() 失败,原始块保持不变;它没有被释放或移动。

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

【讨论】:

  • Linux 手册页记录了 Linux 行为,虽然它们可能符合 C 标准的要求,但也可能受到更多限制。因此,Linux 文档可能会对有关 C 的问题给出误导性的答案,因此不是很好的参考。
  • 问题是问realloc是否会失败,但这个答案似乎是在说明块是否会被移动,这是一个不同的问题。
  • @EricPostpischil 所以最好参考POSIX defintions?
  • 不,对于不特定于 Linux 或 Posix 或特定操作系统的 C 问题,请使用 the C standard
【解决方案2】:

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

由于标准 C 库的规范不需要减少到永不失败,因此健壮的代码不会假定错误是不可能的,即使不太可能。


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

使用等于零的大小参数调用realloc 是一个过时的功能。

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

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

【讨论】:

  • @Pound 定义 MACRO :考虑一个分配系统,它有一个最大数量的分配,我们可以称之为 handles。由于某种原因,realloc 需要在释放旧句柄之前使用新句柄。当使用所有句柄时,任何新大小的realloc() 都会失败,因为分配系统缺少所需的临时句柄。更重要的一点是,C 可以与许多系统一起工作,并且在减少时强制“不失败”根本不是一个重要的要求
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    TL;DR

    不,你不能假设。

    没有理由重新分配()到较小的大小会失败。它正在释放剩余部分。我认为它没有任何失败的理由。

    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,而您将其放入其中的唯一目的是符合标准。而且,无论何时您做某事的唯一目的是符合标准或规范,您通常都会以简单为先。

    【讨论】:

      【解决方案5】:

      没有理由重新分配()到较小的大小会失败。

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

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

      所以这个问题的前提是错误的。

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

      【讨论】:

        猜你喜欢
        • 2011-06-02
        • 2012-03-23
        • 2013-08-23
        • 1970-01-01
        • 2021-08-25
        • 2010-09-19
        • 2016-12-13
        • 2019-09-20
        • 2012-06-12
        相关资源
        最近更新 更多