malloc错误检查方法

10

我看过几种不同的malloc错误检查方式。有一种方式比另一种更好吗?有些退出代码比其他代码更好吗?使用stderr的fprintf比使用printf语句更好吗?使用return而不是exit更好吗?

    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;
}

错误应写入stderr。退出时出现错误,退出状态应为非零。如果malloc失败导致致命错误和立即退出,请考虑将其包装在报告错误并退出的函数中,这样您就不必在代码中检查返回值。 - Tom Karzes
1
不要强制转换 malloc 的返回值。 - Jonathon Reinhart
1
这与malloc特别相关吗?很多事情都可能失败。 - user253751
@rockstar797 不要忘记在问题解决后将答案标记为已接受;-) - Magix
@rockstar797 哈哈,这是一个常见的问题 :p 你应该接受解决了你问题的答案,但在这里,我理解可能会有点混淆。 - Magix
显示剩余2条评论
5个回答

13

这个包装怎么样:

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,不必担心错误检查。

许多程序并没有优雅地处理内存分配失败的情况,这个解决方案对于这些应用程序来说是完全可以的。如果您的应用程序可以在内存分配失败后继续运行,那么您可能不会提出这个问题。


下投票者能否解释一下为什么您不喜欢这个解决方案? - Jonathon Reinhart
我投了反对票,因为根据上下文,应用程序在离开之前可能需要释放一些变量,即使在不需要在分配失败后继续的应用程序中也是如此。这对于这个包装器来说是不可能的,可能会导致内存泄漏。 - Magix
1
我没有点踩,但在分配失败后中止程序并不是“安全”的,因为你还有其他需要释放的内存... 除此之外,您还可能希望将 printf() 格式字符串中的 %zd 更改为 %zu,以匹配 size_t 而不是有符号等效项(在 POSIX 系统上为 ssize_t)。 - user539810
2
@Magix 内存泄漏是您的最小问题,当您的应用程序没有内存时。无论如何,当进程退出时,所有进程的内存都将返回给内核。 - Jonathon Reinhart
1
@Chrono 谢谢,已更改为 %zu。但是,在中止之前释放内存是毫无意义的。 - Jonathon Reinhart
显示剩余4条评论

10
当您使用 malloc(), calloc()realloc() 函数检测到错误(即它们返回 NULL 指针)时,POSIX98 标准规定必须设置 errno (请参见 man malloc)。然后,您可以使用标准函数 perror() 来打印错误信息,无需自己进行格式化。注意它会自动打印到 stderr,不需要自己处理。
此外,如果您的应用程序将错误视为致命错误,则必须结束进程。如果您的代码位于 main() 函数中,则使用 return EXIT_FAILURE; 即可,如果不是,则使用 exit(EXIT_FAILURE);。在这种情况下,不建议使用自己的返回代码退出。
如果错误不被视为致命错误,则由您决定如何处理。
还请注意,当 realloc() 失败并返回 NULL 时,旧指针仍然有效,因此在离开之前必须使用 free 函数释放它。

1
我不会强行规定分配失败一定要被视为致命错误。这完全取决于应用程序。应用程序可能有恢复的方法,比如释放一些内存并重新尝试分配,或者使用较小的分配重试,甚至只是等待一段时间后在内存使用跨应用程序临时的情况下再次尝试。 - kaylum
@M.M 我的意思不是要使用 errno 来检查错误,而只有在检测到错误时 perror() 才会发挥作用。我会进行编辑以使这一点更清晰。 - Magix
更新:实际上,Posix98标准强制设置errno,如我的答案所述。请参阅man malloc和posix标准。不符合规范的实现是未定义行为。 - Magix

2
这不是关于如何检查错误,而是关于你如何处理错误。在所有情况下,你可以看到使用的共同代码片段是: if (ptr == NULL) {....} 当你遇到返回值为NULL时,你之后的操作取决于你个人的选择。一些开发者甚至喜欢使用assert()程序。
同时,gcc会为你设置errno。因此,你可以使用它来获取更多详细信息并将其用于自己的程序中。
总之,你可以根据自己的程序需要进行任何适合自己的操作。

1

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

规则 #2. 除非你可以优雅地恢复,否则始终将错误消息打印到 stderr,并提供相关信息。

规则 #3. 如果出现错误,则始终以非零状态退出。(确切的非零值并不太重要。)

如果你的程序可以容忍突然退出(即,没有保存任何可能是关键的内存数据),那么“包装器”函数是同时满足所有三个规则的绝佳方式。

虽然有一些例外情况适用于规则 #2,但它们仍然涉及某种方式报告错误(只是不一定立即使用 fprintfstderr),因此基本规则仍然有效。


0
perror("more details"); 

将(尝试)按照errno打印错误到stderr。您可以使用它。

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

你也可以保存 errno 并将其转换为 BSD 风格的 sysexits.h 代码,或者只是线性地序列化它到父进程(通过使用 errno 或其线性翻译来退出)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接