另一种在C



考虑这个程序:

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. 具有指向函数的本地指针

NULL上的free被定义为安全no-op。因此,一个非跳跃变化可能是:

int *i = malloc(sizeof(int)); 
int *j = malloc(sizeof(int));
if(i && j)
{
    // do some work
}
free(i);
free(j);

首先,这将不会检测malloc故障:

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

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

其次,atexit有利于清理静态和全局对象。仅在atexit中使用局部对象来使其全局不是一个好主意。

一个更好的方法是为所有相关指针创建一个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;
}
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语句解决的问题。

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);
}

避免多个出口点。避免交错分配和错误处理。遵循干净的操作顺序:

  1. 声明、分配和初始化资源
  2. 如果全部成功,就完成任务
  3. 清理
  4. 退货状态

 // 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;
}
    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被认为是一种糟糕的编程实践但在这里,我们可以使用它轻松简单地完成任务

相关内容

  • 没有找到相关文章

最新更新