【问题标题】:Should we check if memory allocations fail?我们是否应该检查内存分配是否失败?
【发布时间】:2011-12-17 22:10:31
【问题描述】:

我见过很多代码在分配时检查 NULL 指针。这使得代码冗长,如果没有一致地完成,只有当程序员喜欢它时,甚至不能确保当地址空间用完时程序不会崩溃。此外,如果程序不能进行更多的分配,它无论如何也无法完成它的功能,对吧?

所以我的问题是,对于大多数程序来说,根本不检查,如果内存用完就让程序崩溃不是更好吗?至少这样代码更易读。

注意

我说的是在现代计算机上运行的桌面应用程序(至少 2 GB 地址空间),而且绝对不会运行航天飞机、生命支持系统或 BP 的石油平台。最重要的是,我说的是使用 malloc 但内存使用量从未真正超过 5 MB 的程序。

【问题讨论】:

  • 您应该如何知道程序崩溃的原因?是 NULL 指针取消引用还是一些微妙的东西?
  • 是的,让我们都忘记检查内存是否已成功分配。段错误更有趣!
  • 检查malloc 回报的倡导者中,有谁见过它在托管环境中触发?我真的很想知道这仍然有意义的情况。
  • @Jens:这是有道理的,因为这样做不会造成任何损失,而且有一天它可以防止太空火箭爆炸,杀死机上八名宇航员。
  • @TomalakGeret'kal,“托管”与“嵌入”是 C 标准中的精确术语,描述了应用程序可以从 C 库中获得的支持量。

标签: c error-handling malloc


【解决方案1】:

始终检查返回值,但为了清楚起见,通常将 malloc() 包装在从不返回 NULL 的函数中:

void *
emalloc(size_t amt){
    void *v = malloc(amt);  
    if(!v){
        fprintf(stderr, "out of mem\n");
        exit(EXIT_FAILURE);
    }
    return v;
}

那么,以后就可以使用了

char *foo = emalloc(56);
foo[12] = 'A';

没有良心。

【讨论】:

  • 谢谢,您的回答是两全其美,它提供了一个干净的退出,同时不会使代码不必要地冗长。
  • @harris !vv == NULL 一样有效
【解决方案2】:

是的,您应该检查来自malloc 的空返回值。即使您无法从内存分配失败中恢复,您也应该明确退出。像内存分配成功一样继续进行会使您的应用程序处于不一致的状态,并且可能会导致“未定义的行为”,应该避免这种情况。

例如,您最终可能会将不一致的数据写入外部存储,这可能会阻碍应用程序下次运行的恢复能力。以更可控的方式快速退出会更安全。

许多想要在分​​配失败时退出的应用程序将malloc 包装在一个检查返回值并在失败时显式中止的函数中。

可以说,这是 C++ 默认 new 方法在分配失败时抛出异常的优势之一。内存分配失败时无需努力退出。

【讨论】:

  • 虽然按照标准取消引用空指针是 UB,但我编写过的所有系统都可以很好地使用它。也就是说,它们只是崩溃了,与您在调用abort 时观察到的并没有太大区别。而且空指针引用通常并不难调试。无论如何,我想我从未见过因为这个原因而导致程序崩溃,现在地址空间是巨大的。
  • @JensGustedt:如果我尝试分配一大块内存,然后先写入该空间末尾的块,那么我最初不会在 malloc 时取消引用空指针失败。如果我不检查malloc 的返回值,我很有可能在导致分段违规之前能够在其他一些内存上乱涂乱画。谁知道会发生什么。
  • @JensGustedt:我不确定你在争论什么。如果我malloc 20MB 成功,我可以按我喜欢的任何顺序访问这 20MB。如果一个系统仅仅因为我没有按顺序访问分配的内存就允许做危险的事情,那么就会出现严重错误。如果malloc 失败并且我以“null + 19MB”访问内存,我将犯一个严重错误,因为它可能是我地址空间中的有效空间,但与其他一些关键数据重叠。
  • @JensGustedt:你是在问我是否见过malloc 在桌面系统上返回 0?是的,我偶尔会看到它发生,但为什么我看到的对你来说很重要?
  • @JensGustedt:许多现代程序仍然只有 2GB 的可用地址空间,即使它们在具有更大系统地址空间的系统上运行。很多很多程序最终处理的数据比他们预想的要处理的要多。您不必为 20MB 分配失败而耗尽地址空间;您可以通过严重分割地址空间来使其失败。我经常看到这种情况发生。
【解决方案3】:

与上面 Dave 的方法类似,但添加了一个自动传递的宏 文件名和行号到我们的分配例程,以便我们可以报告 发生故障时的信息。

#include <stdio.h>
#include <stdlib.h>

#define ZMALLOC(theSize) zmalloc(__FILE__, __LINE__, theSize)

static void *zmalloc(const char *file, int line, int size)
{
   void *ptr = malloc(size);

   if(!ptr)
   {
      printf("Could not allocate: %d bytes (%s:%d)\n", size, file, line);
      exit(1);
   }

   return(ptr);
}

int main()
{
   /* -- Set 'forceFailure' to a non-zero value in order to observe
         how 'zmalloc' behaves when it cannot allocate the
         requested memory -- */

   int bytes        = 10 * sizeof(int);
   int forceFailure = 0;
   int *anArray     = NULL;

   if(forceFailure)
      bytes = -1;

   anArray = ZMALLOC(bytes);

   free(anArray);

   return(0);
}

【讨论】:

    【解决方案4】:

    但如果您不记录 malloc 失败的位置,则故障排除会困难得多。

    在第XX行分配内存失败是宁愿不崩溃。

    【讨论】:

    • 您曾经见过因为malloc 失败而不是因为程序逻辑根本没有执行malloc 而导致程序崩溃。在嵌入式系统中?我会很好奇。
    【解决方案5】:

    您绝对应该检查 malloc 的返回值。有助于调试并且代码变得健壮。

    Always check malloc'ed memory?

    【讨论】:

      【解决方案6】:

      如今,在托管环境中检查 malloc 的返回错误并没有多大意义。大多数机器都有一个 64 位的虚拟地址空间。你需要很多时间来耗尽它。您的程序很可能会在完全不同的地方失败,即当您的物理+交换内存耗尽时。在那之前它会表现出完全荒谬的表现,因为它只是在交换并且用户会在你到达那里很久之前触发Cntrl-C

      在空指针引用上“很好地”进行分段错误将是一个清楚的点,可以清楚地了解调试器中的故障点。但在我的实践中,我从未将失败的malloc 视为原因。

      为嵌入式系统编程时,情况会完全改变。在那里你绝对应该检查失败的malloc

      编辑:在编辑问题后澄清这一点。那里描述的那种程序/系统显然不是“嵌入式的”。我从未见过malloc 在此处描述的情况下失败。

      【讨论】:

        【解决方案7】:

        我想补充一点,即使您认为边缘情况是安全的或不会导致崩溃以外的其他问题,也应始终检查边缘情况。可能会利用空指针取消引用 (http://uninformed.org/?v=4&a=5&t=sumry)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-28
          • 1970-01-01
          • 2010-12-07
          • 1970-01-01
          • 2020-03-07
          相关资源
          最近更新 更多