如何在C中正确处理malloc失败,尤其是当有多个malloc时?

Rou*_*oun 32 c memory memory-leaks memory-management

假设这是我的代码的一部分:

 int foo()
 {  
    char *p, *q ;
    if((p = malloc(BUFSIZ)) == NULL) {
        return ERROR_CODE;
    }
    if((q = malloc(BUFSIZ)) == NULL) {
        free(p)
        return ERROR_CODE;
    }
    /* Do some other work... */

    free(p);
    free(q);  
 }
Run Code Online (Sandbox Code Playgroud)

由于第一个可能malloc成功但第二个失败,我free(p)在第二个"错误处理程序"中使用.但是,如果有更多malloc的,如果我想修改代码(调整他们的订单,添加或删除一些代码malloc)怎么办?

我知道在C++中有RAII和异常安全等等.但总的来说,malloc在C中处理故障的正确方法是什么?(也许用一些goto?)

abl*_*igh 35

你的代码很好,但对于很多变量,我更喜欢:

int
foo()
{
    char *p = NULL;
    char *q = NULL;
    int ret = 0;

    if (NULL == (p = malloc(BUFSIZ)))
    {
        ret = ERROR_CODE;
        goto error;
    }

    // possibly do something here

    if (NULL == (q = malloc(BUFSIZ)))
    {
        ret = ERROR_CODE;
        goto error;
    }

    // insert similar repetitions

    // hopefully do something here

  error:
    free (p);
    free (q);
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

请注意,释放NULL被定义为无操作.

这避免nn变量的缩进级别.您可以类似地清理文件句柄等(尽管您必须在周围设置条件close()).

现在,如果你知道你可以一次性分配它们,那么dasblinkenlight有一个很好的答案,但这是另一种方式:

int
foo()
{
    int ret = 0;
    char *p = malloc(BUFSIZ);
    char *q = malloc(BUFSIZ);
    char *r = malloc(BUFSIZ);
    if (!p || !q || !r)
    {
        ret = ERROR_CODE;
        goto exit;
    }

    // do something

  exit:
    free(p);
    free(q);
    free(r);
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

最终可能性:如果您确实想要在malloc失败时退出程序,请考虑使用mallopt's M_CHECK_ACTION选项.这会使malloc()故障得到检查和调用abort(),可能会打印出有用的消息.

从手册页:

名称

mallopt - 设置内存分配参数

概要

  #include <malloc.h>

  int mallopt(int param, int value);
Run Code Online (Sandbox Code Playgroud)

描述

mallopt()函数调整控制内存分配函数行为的参数(请参阅参考资料malloc(3)).的param参数指定要修改的参数,以及value指定该参数的新值.

可以为以下值指定以下值param:

M_CHECK_ACTION

设置此参数可控制glibc在检测到各种编程错误时的响应方式(例如,将相同的指针释放两次).分配给此参数的值的3个最低有效位(2,1和0)确定glibc行为,如下所示:

位0:如果该位置1,则打印单行消息stderr,提供有关错误的详细信息.消息以字符串开头"*** glibc detected ***",后跟程序名称,检测到错误的内存分配函数的名称,错误的简要描述以及检测到错误的内存地址.

位1:如果该位置1,则在打印由位0指定的任何错误消息后,程序将通过调用终止abort(3).在自2.4之后的glibc版本中,如果还设置了位0,则在打印错误消息和中止之间,程序还以方式打印堆栈跟踪backtrace(3),并以/proc/[pid]/maps(参见proc(5))的方式打印进程的内存映射.

位2 :(因为glibc 2.4)只有当位0置位时,该位才有效.如果设置了该位,则简化描述错误的单行消息,以仅包含检测到错误的函数的名称以及错误的简要描述.

  • 如果您真的讨厌 GOTO 或者您的公司不允许这样做,您可以轻松地将其替换为 do{}while(false) 并 break (2认同)

das*_*ght 25

既然是完全可以通过NULLfree(),你可以分配你的"直线"所需要的一切,在单杆检查一切,然后免费的一切,一旦你做了,不管你是否拥有实际所做的任何工作:

char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (p && q && r) {
    /* Do some other work... */
}
free(p);
free(q);
free(r);
Run Code Online (Sandbox Code Playgroud)

只要没有中间依赖关系,即没有具有多级依赖关系的结构,这就可以工作.当你这样做时,最好定义一个释放这种结构的函数,而不要假设所有的内存块都是非结构的NULL.

  • 这种方法的真正好处是`malloc()`和`free()`在_same_级别的代码中,每1`malloc()`只存在1`free()`.当然不总是一种可行的方法,但如果可能的话,它是干净和可维护的. (2认同)

use*_*109 5

对于大量分配,我会投入时间创建一个内存管理器来跟踪分配。这样,无论函数是否成功,您都不必担心泄漏。

总体思路是创建一个包装器来malloc记录成功的分配,然后根据请求释放它们。要释放内存,您只需将特殊大小传递给包装函数即可。0如果您知道实际的分配都不会用于指定大小的块,则使用大小来释放内存是合适的0。否则,您可能希望用作~0ULL请求释放大小。

下面是一个简单的示例,允许在两次释放之间最多进行 100 次分配。

#define FREE_ALL_MEM 0

void *getmem( size_t size )
{
    static void *blocks[100];
    static int count = 0;

    // special size is a request to free all memory blocks
    if ( size == FREE_ALL_MEM )
    {
        for ( int i = 0; i < count; i++ )
            free( blocks[i] );
        count = 0;
        return NULL;
    }

    // using a linked list of blocks would allow an unlimited number of blocks
    // or we could use an array that can be expanded with 'realloc'
    // but for this example, we have a fixed size array
    if ( count == 100 )
        return NULL;

    // allocate some memory, and save the pointer in the array
    void *result = malloc( size );
    if ( result )
        blocks[count++] = result;

    return result;
}

int foo( void )
{
    char *p, *q;

    if ( (p = getmem(BUFSIZ)) == NULL ) {
        return ERROR_CODE;
    }
    if ( (q = getmem(BUFSIZ)) == NULL ) {
        getmem( FREE_ALL_MEM );
        return ERROR_CODE;
    }

    /* Do some other work... */

    getmem( FREE_ALL_MEM );
    return SUCCESS_CODE;
}
Run Code Online (Sandbox Code Playgroud)