如何检测malloc失败?

35

如何使用可移植的方式检查malloc是否未成功分配非零内存块?

5个回答

42

15
errno 是线程安全的。不要听信告诉你相反的人。 - R.. GitHub STOP HELPING ICE
11
引用文献是XBD 3.396:http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396 线程是进程中的单个控制流。每个线程都有自己的线程ID、调度优先级和策略、错误码值、线程特定的键/值绑定以及支持控制流所需的系统资源。 - R.. GitHub STOP HELPING ICE
1
malloc() 在分配失败时会返回 NULL。即使 OP 使用 "分配非零内存块" 进行了豁免,malloc(0) 也可能返回 NULL - chux - Reinstate Monica
3
C语言标准没有要求在malloc()失败时设置errno,因此一般情况下不能依赖它。 - Toby Speight

22

我总是这样做:

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。 真正的问题是,你没有从编译器获得任何警告。

然后你高高兴兴地把那个int转换成了char* (通过强制类型转换)。 在sizeof(char*)与sizeof(int)不同的机器上, 这段代码是严重错误的。

现在,如果你只是写 char *var = malloc(10); 而忘记了包含头文件, 编译器会给出警告。"


4
дҢ д№џеғ”иҮӨйЃүе…Қ写sizeof(char)гЂ‚еЏҮд»ӨдҢүз”Ёsizeof *tokжқӨд»Әж›үпәЊж€–者干脆дёҚ写。 - R.. GitHub STOP HELPING ICE
1
使用sizeof(datatype)不会使它在使用不同大小的该数据类型的系统上兼容吗? - Swift
5
除了这不是任意数据类型,它是 char,而 sizeof 是以 char 为单位的。根据定义,sizeof(char) 为1,从概念上讲,这类似于单位误差。无论如何,使用 sizeof *dest_ptr 更好,因为如果您将 dest_ptr(此处为 tok)的类型更改为其他类型,它会自动调整。 - R.. GitHub STOP HELPING ICE
5
是的,如果使用int,需要乘以sizeof(int)。但是,直接从指向类型大小中获取大小最安全,例如在你的示例中使用sizeof *tok。这样,如果更改类型并忘记更新malloc调用,就不会传递错误的大小。 - R.. GitHub STOP HELPING ICE
1
由于这是SO,我们必须严谨,OpenBSD的人建议不要使用乘法来确定malloc大小,因为它可能会导致整数溢出在未经过良好消毒的输入上。他们定义了reallocarray来处理这个问题,但更便携的解决方案可能是使用calloc。当然,对内存进行清零会有性能损失,但通常可以忽略不计。 - bgw
显示剩余2条评论

9
当以下情况发生时,您可以检测到分配失败:

malloc(n)返回NULL

这是最常见和可靠的测试方法来检测分配失败。如果您想要超出POSIX/SUS的可移植性,我不会相信errno。如果您需要详细信息,例如记录日志,我会在调用之前将errno设置为0,看看它是否更改,然后可能记录下来。

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

尝试访问该地址并查看操作系统是否关闭进程。是的,这种情况可能发生。这被称为内存过度承诺,类似于银行的准备金制度。这是操作系统或虚拟机器的乐观方法,它们会返回地址到虚拟内存,他们赌气他们永远不必提供实际内存。这在LinuxVMware上发生。(我找不到任何显式的Windows过度承诺证据,尽管请求的页面只有在访问时才“提交”。)
那么问题来了,“如何可移植地检测我的程序是否即将在访问我之前信任的malloc获取的地址时崩溃?”一种方法可能是read()从一个随机文件读取到测试区域并查看操作系统是否返回EINVAL或等效值。
对于额外的分数,

malloc(0)返回NULL,并使errno未定义

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

7
当然。一种可移植的方法是检测 malloc(...) 是否返回NULL

4

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

为了检测失败:

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

注意:

就像在提问者的情况中:“……分配非零内存块”,通常代码是这样的,即不能出现0的分配请求,因此不需要进行0测试。

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

一些系统在失败时会设置errno,但并非所有系统都是这样。在C11规范中,由于内存分配失败而设置errno是没有明确定义的。

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


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