【问题标题】:How detect malloc failure?如何检测malloc失败?
【发布时间】:2011-09-13 15:11:42
【问题描述】:

检查malloc是否分配非零内存块失败的可移植方法是什么?

【问题讨论】:

    标签: c


    【解决方案1】:

    根据Single Unix Specification,malloc会在失败时返回NULL并设置errno。

    【讨论】:

    • errno 是线程安全的。不要听别人说其他话的人。
    • 引用是 XBD 3.396:pubs.opengroup.org/onlinepubs/9699919799/basedefs/… 进程中的单一控制流。每个线程都有自己的线程 ID、调度优先级和策略、errno 值、特定于线程的键/值绑定以及支持控制流所需的系统资源。
    • malloc() 在失败时返回 NULL。即使 OP 用“分配非零内存块”来免除它,malloc(0) 也可以返回NULL。
    • C 标准没有要求在 malloc() 失败时设置 errno,因此在一般情况下不能依赖它。
    【解决方案2】:

    我总是这样做:

    tok = malloc( sizeof( char ) * ( strlen(tc) + 1 ) );
    
    if( tok == NULL )
    {
         /* Malloc failed, deal with it */
    }
    

    有些人会使用tok = (type) malloc( ... ),但您应该转换结果,因为显然它掩盖了一些令人讨厌的错误。我会做一些研究,看看我能不能找出它们到底是什么。

    编辑:

    铸造 malloc 可以隐藏一个缺失的#include <stdlib.h>

    我发现这个链接包含一个很好的解释:

    http://cboard.cprogramming.com/faq-board/25799-faq-casting-malloc.html

    “所以当你说这个时 (char*)malloc(10)

    你是说你拿什么 malloc 返回,将其转换为 char*, 并将其分配给变量 问题。

    如果 malloc 是,这一切都很好 原型正确(通过包括 stdlib.h),它被定义为 返回 void*。

    当你失败时问题就来了 包括 stdlib.h 和编译器 最初假设 malloc 返回 一个整数。真正的问题是,你不要 从编译器获得任何警告。

    然后您可以愉快地将 int 转换为 char* (通过演员表)。在机器上 其中 sizeof(char*) 不同于 sizeof(int),代码很严重 坏了。

    现在如果你只有 char *var = malloc(10);而你错过了 包括,你会得到一个 来自编译器的警告。”

    【讨论】:

    • 你也应该避免写sizeof(char)。要么使用sizeof *tok,要么什么都不用。
    • 不会使用 sizeof(datatype) 使其在对该数据类型使用不同大小的系统之间兼容吗?
    • 除非这不是任意数据类型,它是char,而sizeof 以字符为单位。 sizeof(char) 根据定义为 1,从概念上讲,它类似于单位错误。无论如何,使用sizeof *dest_ptr 是一种更好的做法,因为如果您将dest_ptr(此处为tok)的类型更改为其他类型,它会自动调整。
    • 是的,int 需要乘以 sizeof(int)。但同样,直接从所指向的类型的大小中获取大小是最安全的,即您的示例中的 sizeof *tok。然后,如果您更改类型并忘记更新 malloc 调用,您将不会传递错误的大小。
    • 既然如此而且我们必须是迂腐的,OpenBSD 的人建议不要用乘法来确定 malloc 的大小,因为它可能会导致在处理不当的输入上出现整数溢出。他们定义了reallocarray 来处理这个问题,但是对于这个问题更便携的解决方案可能是使用calloc。当然,将内存归零会影响性能,但这通常可以忽略不计。
    【解决方案3】:

    您可以在以下情况下检测到故障:

    malloc(n) 返回NULL

    这是检测分配失败的最常见和最可靠的测试。如果您想在 POSIX/SUS 之外移植,我不相信errno。如果您需要详细信息,比如说记录,我会在通话前将errno 归零,看看它是否发生了变化,然后可能会记录下来。

    malloc(n) 返回一个非NULL 地址,该地址不受实际内存支持

    触摸它,看看你是否被操作系统杀死。是的,这可能发生。它被称为memory overcommit,类似于fractional reserve banking。这是操作系统或管理程序将地址返回到他们正在赌博的虚拟内存的乐观方法,他们永远不必实际提供。这发生在Linux、VMware。 (尽管请求pages are only "committed" when they are touched,但我找不到任何 Windows 过度使用的明确证据。)

    然后的问题是“我如何可移植地检测我的程序是否将在访问我从以前信任的 malloc 获得的地址时崩溃,就像青少年迷恋一样?”。一种方法可能是 read() a random file into the test area 并查看操作系统是否返回 EINVAL 或等效项。

    加分,

    malloc(0) 返回 NULL 并留下 errno 未定义

    我知道“非零 [大小] 内存块”的问题,但这很有趣。考虑一个符合 SUS 的分配器,它打算返回非NULL 用于零大小的分配(它可以这样做),但随后它失败了,因此它必须返回NULL。它可以尝试设置errno。那是失败吗?我认为 Hoare 说我们为这种模棱两可付出了 10 亿美元。所以,调用malloc(0) 是不可移植的,提问者可能知道这一点!

    【讨论】:

      【解决方案4】:

      当然。可移植的方法是测试malloc(...) 是否返回NULL。

      【讨论】:

        【解决方案5】:

        malloc(n) 在失败时返回 NULL。
        malloc(0) 可能返回 NULL。

        检测故障:

        void* ptr = malloc(n);
        if (ptr == NULL && n > 0) Handle_Failure();
        

        注意事项:

        在 OP 的情况下:“...分配非零内存块”,通常代码是这样的,0 分配请求不会发生,因此不需要0 测试。

        size_t nstr = strlen(some_string) + 1;
        void* ptrstr = malloc(nstr);
        if (ptrstr == NULL) Handle_Failure();
        

        一些系统在失败时设置errno,但不是全部。由于内存分配失败而设置errno 未在 C11 规范中指定。

        malloc(n) 期望 n 是无符号类型 size_t。使用带有负值的 int n 肯定会转换为一些大的无符号值,然后可能会导致内存分配失败。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-12-15
          • 1970-01-01
          • 2013-08-26
          • 2015-06-19
          • 1970-01-01
          • 1970-01-01
          • 2011-11-05
          相关资源
          最近更新 更多