使 malloc() 返回 NULL 而不是使程序崩溃?

Posted

技术标签:

【中文标题】使 malloc() 返回 NULL 而不是使程序崩溃?【英文标题】:Make malloc() return NULL instead of crashing the program? 【发布时间】:2011-03-31 21:40:51 【问题描述】:

我正在使用malloc 在 C 程序中分配内存。我的程序分配的内存可能超出系统的空间,此时程序崩溃。就我的目的而言,如果malloc 只返回NULL 会更好(就像它显然是supposed to),这样我就可以捕捉到错误。相反,它的作用是抛出一个错误,提示“现在没有可用于编程的内存:调用 malloc 不安全”。并使程序崩溃。

我该如何解决这个问题?

编辑:我知道程序自己崩溃了,而不是因为我试图引用一个空指针。该程序从不直接调用malloc,而是调用我编写的调用malloc的函数,然后检查它是否返回NULL。从来没有说malloc返回了NULL

编辑 2:如果有帮助,这里是完整的错误输出:

节目接收信号:“EXC_BAD_ACCESS”。 共享库应用加载规则全部 警告:无法恢复之前选择的帧。 数据格式化程序暂时不可用,将在“继续”后重试。 (被调试的程序在从 GDB 调用的函数中发出信号。 GDB 保留在接收信号的帧中。 要更改此行为,请使用“set unwindonsignal on” 将放弃对包含函数 (dlopen) 的表达式的求值。) 现在没有可用于编程的内存:调用 malloc 不安全

【问题讨论】:

取决于您的操作系统。您使用的是 Linux、Mac 还是 Windows? Mac OS X 雪豹。 (我不确定操作系统版本是否重要,但知道也无妨。) 你能发布你调用 malloc 的函数吗? 不要责怪图书馆。您的 malloc 可能工作正常,直到您的代码中的错误在堆上乱涂乱画,这让 malloc 很难过。 GWW:太长了。但是,相关部分如下所示: ptr = malloc(...); if (ptr == NULL) /* 错误处理 */ else return ptr; (编辑:抱歉格式化,如何缩进?) 【参考方案1】:

一旦你通过缓冲区溢出、野指针或其他错误在堆上乱涂乱画,malloc 的行为就会变得未定义,它可能会返回任何东西。

Malloc 只是一个用户空间库;里面没有任何魔法。如果我在您的应用程序的客户名称链接列表上乱涂乱画,那么当您稍后访问该列表时,您会遇到奇怪的行为。 Malloc 的行为方式相同,但由于 malloc 的使用是通过代码分布的,因此因果关系具有全局范围。

所有的答案都围绕着这样一个事实,即指针错误是 C 代码中最普遍的缺陷来源。你很幸运,你得到了一个 SIGBUS,它是缺陷的证据,它可能与故障发生的地点和时间相距甚远。使用valgrind 帮助您找到真正的缺陷在哪里。

【讨论】:

+1 用于 valgrind。如果malloc() 正在做不可能的事情,那么您可能无意中损坏了一些堆。 谢谢。我将安装 Valgrind。你让我觉得可能正在发生的事情是我将太多的东西放入一个数组中并且它正在结束。 糟糕,OS 10.6 似乎不支持 Valgrind。除了安装不同的操作系统,你知道在 10.6 上运行的任何类似程序吗? 唉,我上次接触 Mac 时,OS 8 是新的闪亮。您可能想开始新的“如何在 MacOS X.6 上捕获 C 指针错误?”问题很快就会变得陈旧。 @msw:我读过编译器省略了malloc/free 调用。该标准没有说明 C 编译器如何与底层平台交互。尽管此类行为与此类平台的记录行为相矛盾,但在没有平台限制的情况下,标准本身并没有说明会禁止它。【参考方案2】:

它检查 Malloc 是否返回 NULL?您可能对相等性测试有疑问。尝试类似 'if (malloc(...)) then ...;别的 ...;'而不是特定的检查。

如果这没有帮助,那么只运行预处理器并计算出正在“编辑”的 NULL 是什么。

【讨论】:

"if (malloc(...)) then ...; else ..." 有同样的问题。 NULL 被编辑为 (void *)0,这是你所期望的,不是吗? 是的,但可能是对 NULL 的错误重新声明,使它变成了别的东西。我们从标准中知道非 NULL 指针是“真的”,所以这是一个合理的死鸡来解决这个问题......【参考方案3】:

根据this apple page,如果在xcode中打开MallocErrorAbort环境变量,程序将基于malloc错误中止。要关闭此变量,请右键单击树视图中的可执行文件,选择“获取信息”,然后转到“参数”选项卡。

【讨论】:

原来是没有的,所以我把它放进去设置为0,问题没有解决。当我从命令行而不是 Xcode 编译程序时,问题仍然存在。另外,感谢您的链接。 然后我将 MallocErrorAbort 设置为 YES,编译器给了我一条消息,表明它已打开。程序以同样的方式崩溃。【参考方案4】:

奇怪...如果我从命令行编译它,以下对我有用。 malloc 提供的运行时确实有 a lot of options as Spencer mentioned。如果您使用的是 XCode,我会寻找一个控制它的选项。

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


int
main()

    signed long long alloc_sz = (1 * 1024 * 1024 * 1024);
    while (alloc_sz > 0ull) 
        char *ptr = malloc(alloc_sz);
        if (ptr == NULL) 
            fprintf(stderr, "failed to allocate %llu bytes\n",
                    (unsigned long long)alloc_sz);
            break;
         else 
            free(ptr);
            fprintf(stderr, "allocated %llu bytes\n",
                    (unsigned long long)alloc_sz);
        
        alloc_sz *= 2ull;
    
    return 0;

顺便说一句: 这是在 debug 还是 release 构建中?可能是 XCode 试图以某种方式帮助您。

【讨论】:

我运行了那个代码,它给出了一些输出,然后malloc 返回了 NULL。现在我们知道是其他原因导致了问题。顺便说一句:调试构建。【参考方案5】:

正如其他人所说,很可能您已经覆盖了malloc's 的一些内部状态,导致它做一些奇怪的事情。

在 Linux 上,您可以使用valgrind 来追踪它。如果您的代码具有足够的跨平台能力,可以在 Linux(可能在 VM 中)上运行,我建议您这样做。

如果您无法使用此选项,Mac OS X 有自己的内置调试工具。在高层次上有仪器。在较低级别,malloc 将启用调试以响应某些环境变量: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/malloc.3.html 向下滚动到ENVIRONMENT 部分。因此,例如,您将使用

运行您的程序
MallocGuardEdges=1 ./myProg

你也可以使用libgmalloc,就像破解版的malloc调试功能一样。你可以在这里读到它: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/libgmalloc.3.html

你会像这样运行你的程序:

DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib ./myProg

【讨论】:

以上是关于使 malloc() 返回 NULL 而不是使程序崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

聚合函数(MAX 等)返回 NULL 而不是没有行

如何使向上按钮返回而不是打开导航抽屉

如何使单选按钮返回布尔值真/假而不是开/关

如何使 Identity.GetUserId() 返回 Guid 而不是字符串?

使 Web API 身份验证返回 401 而不是重定向到登录页面

如何使授权属性返回自定义 403 错误页面而不是重定向到登录页面