【问题标题】:malloc error checking methodsmalloc 错误检查方法
【发布时间】:2016-01-27 00:53:53
【问题描述】:

我见过几种不同的 malloc 错误检查方法。一种方式比另一种更好吗?某些退出代码是否比其他代码更好?使用带有 stderr 的 fprintf 是否比使用 printf 语句更好?使用返回而不是退出更好吗?

    ptr=(int*)malloc(n*sizeof(int));  //memory allocated using malloc
    if(ptr==NULL)                     
    {
        printf("Error! memory not allocated.");
        exit(0);
    }

    ptr=(int*)malloc(n*sizeof(int));  //memory allocated using malloc
    if(ptr==NULL)                     
    {
        printf("Error! memory not allocated.");
        exit(1);
    }

    res = malloc(strlen(str1) + strlen(str2) + 1);
    if (!res) {
        fprintf(stderr, "malloc() failed: insufficient memory!\n");
        return EXIT_FAILURE;
    }

    ptr=(int*)malloc(n*sizeof(int));  //memory allocated using malloc
    if(ptr==NULL)                     
    {
        printf("Error! memory not allocated.");
        exit(-1);
    }

   ptr=(int*)malloc(n*sizeof(int));  //memory allocated using malloc
    if(ptr==NULL)                     
    {
        printf("Error! memory not allocated.");
        exit(EXIT_FAILURE);
    }

char *ptr = (char *)malloc(sizeof(char) * some_int);
if (ptr == NULL) {
    fprintf(stderr, "failed to allocate memory.\n");
    return -1;
}

char* allocCharBuffer(size_t numberOfChars) 
{
    char *ptr = (char *)malloc(sizeof(char) * numberOfChars);
    if (ptr == NULL) {
        fprintf(stderr, "failed to allocate memory.\n");
        exit(-1);
    }
    return ptr;
}

【问题讨论】:

  • 错误应写入标准错误。当出现错误退出时,退出状态应该是非零的。如果 malloc 失败应导致致命错误并立即退出,请考虑将其包装在报告错误并退出的函数中,这样您就不必检查代码中的返回值。
  • 这本书现已绝版,但 Richard Heathfield 等人的“C Unleashed”有一章有趣的章节介绍了您可以用来尝试从malloc恢复的策略失败。文字处理器的用户不希望应用程序仅仅因为他们正在尝试的操作遇到内存资源限制而崩溃。至少,如果有一些相关的状态概念,也许状态可以在退出之前以某种方式保存。不要认为malloc 失败会自动终止您的程序。
  • 这与 malloc 具体无关,是吗?很多事情都可能失败。
  • @rockstar797 如果您的问题得到解决,请不要忘记添加一个已接受的答案 ;-)

标签: c malloc


【解决方案1】:

这个包装器怎么样:

void *safe_malloc(size_t n)
{
    void *p = malloc(n);
    if (p == NULL) {
        fprintf(stderr, "Fatal: failed to allocate %zu bytes.\n", n);
        abort();
    }
    return p;
}

那就随处使用safe_malloc,不用担心错误检查。

许多程序并不是为了优雅地处理内存分配失败而编写的,这个解决方案对于那些应用程序来说很好。如果您的应用程序在内存分配失败后能够继续运行,那么您可能不会问这个问题。

【讨论】:

  • 反对者是否愿意解释您为什么不喜欢此解决方案?
  • 我投了反对票,因为根据上下文,应用程序可能需要在离开之前free 一些变量,即使在分配失败后不需要继续的应用程序中也是如此。使用此包装器无法做到这一点,并且可能导致内存泄漏。
  • 我没有投反对票,但是当您有其他需要释放的内存时,在分配失败后中止程序几乎不是“安全的”...除此之外,您可能还想更改 %zdprintf() 格式字符串中的 %zu 匹配 size_t 而不是签名的等价物(POSIX 系统上的 ssize_t)。
  • @Magix 内存泄漏是您的应用程序内存不足时最不担心的问题。无论如何,当进程退出时,所有进程的内存都会返回给内核。
  • @Chrono 谢谢,改成 %zu。但同样,在中止之前释放内存是没有意义的。
【解决方案2】:

当您检测到 malloc()calloc()realloc() 的错误(即它们返回 NULL 指针)时,POSIX98 标准规定必须设置 errno(请参阅 man malloc)。然后,您可以使用标准函数perror() 打印错误,而无需进行自己的格式化。请注意,它会自动打印到stderr,无需为此烦恼。

此外,如果您的应用程序认为该错误是致命的,则必须结束该过程。如果您的代码位于main() 函数中,则使用return EXIT_FAILURE; 即可,否则使用exit(EXIT_FAILURE);。在这种情况下,不建议使用您自己的返回码退出。 如果错误不被视为致命错误,则由您决定如何处理。

还请注意,当realloc() 失败并返回NULL 时,旧指针仍然有效,因此在离开之前必须为freed。

【讨论】:

  • 我不会说分配失败总是被认为是致命的。这真的取决于应用程序。应用程序可能有一种恢复方式,例如释放一些内存并重试分配,或者使用较小的分配重试,甚至只是等待一段时间,然后在整个应用程序的内存使用可能是暂时的情况下再试一次。
  • @M.M 我并不是要使用 errno 检查错误,但只有在检测到错误时,perror() 才会完成它的工作。我将进行编辑以使其更清晰
  • 已更新:Posix98 标准实际上强制设置 errno,如我的回答中所述。请参阅man malloc 和 posix 标准。不合规的实现是未定义的行为
【解决方案3】:

这与您如何检查错误无关,而是您如何处理错误。在所有情况下,您都可以看到使用的常见代码是

if (ptr == NULL) {....}

当你遇到返回值是NULL,那之后你做什么是你个人的选择。一些开发者甚至喜欢assert() 该程序。

与此同时,gcc 为您设置了 errno。因此,您可以使用它来获取更多详细信息并使用它。

总之,您可以为您的程序做最适合您的事情。

【讨论】:

    【解决方案4】:

    规则 #1。始终检查 malloc 的返回值(和 realloc 的)是否有错误。

    规则 #2。除非您可以正常恢复,否则请始终将错误消息打印到stderr,并提供相关信息。

    规则 #3。始终以存在错误的非零状态退出。 (确切的非零值并不重要。)

    “包装器”函数是同时处理所有三个规则的绝佳方式,如果您的程序可以容忍突然退出(也就是说,不保存任何内存数据,这可能是至关重要)。

    规则 #2 有一些例外情况,但它们仍然涉及以某种方式报告错误(不一定是立即将 fprintf 发送到 stderr),因此基本规则成立。

    【讨论】:

      【解决方案5】:
      perror("more details"); 
      

      将(尝试)将错误(根据 errno)打印到 stderr。 你可以使用它。

      static void die(const char* msg){ perror(msg); exit(1); }  
      

      您还可以保存 errno 并将其转换为 BSD 样式的 sysexits.h 代码,或者只是以某种方式将其线性序列化到父进程(通过 exiting 与 errno 或其线性转换)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-26
        • 1970-01-01
        • 1970-01-01
        • 2021-08-17
        • 2021-08-03
        • 2020-05-19
        相关资源
        最近更新 更多