测试ErnoMEM的errno,而不是将一系列malloc调用与NULL

时间:2019-03-28 10:05:42

标签: c

通常,一个测试malloc的结果是否不是NULL才能知道内存分配是否成功。通过一系列malloc调用,这将成为冗长或繁琐的比较。

相反,可以在一系列errno = 0调用的顶部设置malloc,然后在末尾测试errno == ENOMEM吗?
这假定如果任何分配失败,则程序或函数将无法继续进行,而必须返回/退出。它还假定malloc调用是连续的和连续的,并且根据手册,malloc只能将errno设置为ENOMEM

示例如下所示:

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

#define N (1 << 20)

int main()
{
        double *a = NULL;
        double *b = NULL;
        double *c = NULL;

        errno = 0;
        a = malloc(N * sizeof *a);
        b = malloc(N * sizeof *b);
        c = malloc(N * sizeof *c);
        if (errno == ENOMEM) {
                perror(NULL);
                free(a);
                free(b);
                free(c);
                return EXIT_FAILURE;
        }
        errno = 0;

        /* Do interesting stuff */

        free(a);
        free(b);
        free(c);
        return EXIT_SUCCESS;
}

(该示例使用main(),但它也可能是另一个无法运行的功能,但是程序可能会继续执行,并且实际上不会退出程序,因此{{1} }通话是必需的。)

我看不出为什么不能安全地完成此操作的任何原因,但这不是我遇到的惯用语,因此是个问题。

2 个答案:

答案 0 :(得分:2)

不,您不能安全地以便携式方式执行此操作。

按照C标准的7.5 Errors <errno.h>, paragraph 3

  

程序启动时,初始线程中errno的值为零(其他线程中errno的初始值为不确定的值),但是任何库函数都不会将其设置为零。 可以通过库函数调用将errno的值设置为非零,前提是在描述中未记录errno的使用。在此国际标准中起作用。

由于malloc()并未被C标准设置为errno,因此可以自由地errno争取成功。

答案 1 :(得分:1)

  

相反,可以在一系列malloc调用的顶部设置errno = 0,然后在最后测试errno == ENOMEM吗?

是的,只要记录到您对malloc的实现将errno设置为ENOMEM即可。 C11标准(第7.22.3.4节)中的规范仅提到返回的指针将是NULL,而不是将设置errno,因此从技术上讲,您的代码不可移植。

在MacOS,Windows和Linux中,malloc的默认实现确实设置了errno,因此世界上大多数计算机都在使用。但是,如果需要真正的可移植性,请最后写

if (a == NULL || b == NULL || c == NULL)
{
    // Handle the failure
}

附录:errno之后,无需将malloc重置为零。