另一种在C中进行清理的方法?

时间:2016-03-24 15:42:08

标签: c pointers malloc free atexit

考虑这个程序:

int main(void)
{
    int* i = malloc(sizeof(int));
    int* j = malloc(sizeof(int));
}

然而,这是一种天真的方法,因为malloc可能会失败并且指针不是free' d。

所以你可以这样做:

int main(void)
{
    int* i; 
    int* j;

    if ((i = malloc(sizeof(int)) < 0)
    {
        return -1;
    }
    if ((j = malloc(sizeof(int)) < 0)
    {
        free(i);
        return -1;
    }

    free(i);
    free(j);
}

但是我觉得这很容易出错。考虑必须分配20个指针,在上一个malloc错误案例中,您必须free 19个变量,然后return -1

我也知道atexit,这可以帮助我这样写:

int* i; 
int* j;

void del_i(void)
{
    free(i);
}

void del_j(void)
{
    free(j);
}

int main(void)
{
    if ((i = malloc(sizeof(int)) < 0)
    {
        return -1;
    }
    else
    {
        atexit(del_i);
    }

    if ((j = malloc(sizeof(int)) < 0)
    {
        return -1;
    }
    else
    {
        atexit(del_j);
    }
}

哪个更好,但我不喜欢将所有指针声明为全局。有没有办法将这两种方法结合起来,基本上是:

  1. 具有指针的析构函数,可以直接执行,也可以与atexit一起使用。
  2. 指向函数的本地指针。

6 个答案:

答案 0 :(得分:4)

free上的

NULL被定义为安全无操作。所以非跳跃变化可能是:

int *i = malloc(sizeof(int)); 
int *j = malloc(sizeof(int));

if(i && j)
{
    // do some work
}

free(i);
free(j);

答案 1 :(得分:3)

首先,这将检测malloc失败:

if ((i = malloc(sizeof(int)) < 0)
{
    return -1;
}

malloc在失败时返回NULL,而不是负数。

其次,atexit适用于清理静态和全局对象。将本地对象设置为全局只是在atexit内使用它们不是一个好主意。

更好的方法是为所有需要在all-or-nothing单元中分配的相关指针创建struct,定义一个函数以便一次释放它们,并编写一个分配它们的函数逐个检查每个分配的内存:

typedef struct AllOrNothing {
    double *dPtr;
    int *iPtr;
    float *fPtr;
    size_t n;
} AllOrNothing;

void freeAllOrNothing(AllOrNothing *ptr) {
    free(ptr->dPtr);
    free(ptr->iPtr);
    free(ptr->fPtr);
    free(ptr);
}

int allocateAllOrNothing(size_t n, AllOrNothing **res) {
    *res = malloc(sizeof(AllOrNothing));
    if (*res == NULL) {
        return -1;
    }
    // Freeing NULL is allowed by the standard.
    // Set all pointers to NULL upfront, so we can free them
    // regardless of the stage at which the allocation fails
    (*res)->dPtr = NULL;
    (*res)->iPtr = NULL;
    (*res)->fPtr = NULL;
    (*res)->n = n;
    (*res)->dPtr = malloc(n*sizeof(double));
    if ((*res)->dPtr == NULL) {
        free(*res);
        *res = NULL;
        return -1;
    }
    (*res)->fPtr = malloc(n*sizeof(float));
    if ((*res)->fPtr == NULL) {
        free(*res);
        *res = NULL;
        return -1;
    }
    (*res)->iPtr = malloc(n*sizeof(int));
    if ((*res)->iPtr == NULL) {
        free(*res);
        *res = NULL;
        return -1;
    }
    return 0;
}

答案 2 :(得分:0)

int main(void)
{
int* i = NULL; // Init with NULL otherwise free on none NULL possible
int* j = NULLL;

if (!(i = malloc(sizeof(int)))
{
    goto exit;
}
if (!(j = malloc(sizeof(int)))
{
    goto exit;
}
...
exit:
    free(i);
    free(j);
    ...
    return err;
}

这是你可以用goto语句解决的问题。

答案 3 :(得分:0)

int main(void)
{
    int* i = NULL; 
    int* j = NULL;
    bool success = false;

    do {
        i = malloc(sizeof(int));
        if (NULL == i) break;

        j = malloc(sizeof(int));
        if (NULL == j) break;

        success = true;
    } while (0);

    if (!success)
    {
        printf("Something failed!");
    }
    else
    {
        printf("All succeeded!");
        // Do more work
    }

    free(i);
    free(j);
    return (success? 0 : 1);
}

答案 4 :(得分:0)

避免多个退出点。避免隔行扫描分配和错误处理。遵循清洁的操作顺序:

  1. 声明,分配和初始化资源..
  2. 如果一切顺利,请完成任务。
  3. 清理。
  4. 退货状态。
  5.  // Do all allocations first, test their `NULL`-ness, then free them all.
     int main(void) {
        // Allocate resources   
        // declare and allocate in one step
        int* i    = malloc(sizeof *i);
        double* j = malloc(sizeof *j);
    
        // Test for acceptability
        bool ok = i && j;
    
        // Perform the main body of code
        if (ok) {
          ; // do normal process in the code;
        }
    
        // free resources
        free(i);
        free(j);
    
        // return status
        return ok ? 0 : -1;
    }
    

答案 5 :(得分:-1)

    int *i=NULL,*j=NULL;

    if(!(i=malloc(sizeof(int))))
            goto EXIT;
    if(!(j=malloc(sizeof(int))))
            goto EXIT;
    /* do some work */
    return 0;
    EXIT:
            free(i);
            free(j);
            exit(EXIT_FAILURE);

虽然goto被认为是一种糟糕的编程习惯 但在这里我们可以用它来轻松简单地完成任务