C中单个函数中处理多个malloc错误的推荐方法

C中单个函数中处理多个malloc错误的推荐方法,c,error-handling,malloc,C,Error Handling,Malloc,建议用什么方法来处理多个malloc错误,这些错误可能会在下面的代码中顺序发生 bool myFunc(int x, int y) { int *pBufX = null; int *pBufY = null; if((x <= 0) || (y <= 0)) { return false; } pBufX = (int*)malloc(sizeof(int) * x); if(pBufX == null)

建议用什么方法来处理多个malloc错误,这些错误可能会在下面的代码中顺序发生

bool myFunc(int x, int y)
{
    int *pBufX = null;
    int *pBufY = null;

    if((x <= 0) || (y <= 0))
    {
        return false;
    }

    pBufX = (int*)malloc(sizeof(int) * x);
    if(pBufX == null)
    {
        return false; 
    }

    pBufY = (int*)malloc(sizeof(int) * y);
    if(pBufY == null)
    {
        free(pBufX) //free the previously allocated pBufX 
        return false; 
    }

    //do something useful

    free(pBufX);
    free(pBufY);

    return true; 
}
boolmyfunc(整数x,整数y)
{
int*pBufX=null;
int*pBufY=null;

如果((x个人而言,我更喜欢使用goto。但是,由于
免费(NULL)
是安全的,您通常可以提前完成所有分配:

int *a = NULL; 
int *b = NULL;
a = malloc( sizeof *a * x);
b = malloc( sizeof *b * y);
if( a == NULL || b == NULL ) {
    free(a); 
    free(b); 
    return false;
}

如果您需要分配的内存来进行计算以获得进一步分配所需的大小,那么您的函数可能非常复杂,因此无论如何都应该对其进行重构。

这是一种可能的解决方案:

bool myFunc(int x, int y)
{
    int returnvalue = false;

    int *pBufX = NULL;   // << null -> NULL
    int *pBufY = NULL;

    if((x <= 0) || (y <= 0))
    {
        goto fail;
    }

    pBufX = (int*)malloc(sizeof(int) * x);
    if(pBufX == null)
    {
        goto fail; 
    }

    pBufY = (int*)malloc(sizeof(int) * y);
    if(pBufY == null)
    {
        goto fail;
    }

    //do something useful

    returnvalue = true;    

fail:
   free(pBufX);   // <<< free(x) -> free(pBufX)
   free(pBufY);   // <<< free(y) -> free(pBufY)

   return returnvalue;
}
可替换为:

  free(pBufY);

在我看来,最合适的方法是将核心功能移动到单独的功能:

inline bool doStuff (int x, int y, int* pBufX, int* pBufy)

bool myFunc (int x, int y)
{
  bool result;
  int *pBufX = NULL;
  int *pBufY = NULL;

  /* do allocations here if possible */

  result = doStuff(x, y, pBufX, pBufY); // actual algorithm

  free(pBufX);
  free(pBufY);

  return result;
}
现在,您只需要在出错时从
doStuff
返回,而不必担心释放。理想情况下,您可以在包装函数中执行malloc,从而将内存分配与实际算法分离,但有时这是不可能的

注意,free保证在您向它传递空指针时它不会做任何事情

编辑:

请注意,如果您在
doStuff
中进行分配,您需要将指针传递给指针!

这是一个很好的问题!(我想我会发现一个重复的问题,但不,奇怪的是,C中如此重要的一个方面似乎以前从未真正提出过)

我发现有两个问题涉及这一领域的一些方面:

这些主要集中在转到方式上。所以首先让我们讨论一下

转到方式

如果您有一个依赖于分配若干资源的代码,这些资源必须在以后发布,那么您可以使用如下模式:

int function(void){
    res_type_1 *resource1;
    res_type_2 *resource2;

    resource1 = allocate_res_type_1();
    if (resource1 == NULL){ goto fail1; }
    resource2 = allocate_res_type_2();
    if (resource2 == NULL){ goto fail2; }

    /* Main logic, may have failure exits to fail3 */

    return SUCCESS;

    fail3:
    free_res_type_2(resource2);
    fail2:
    free_res_type_1(resource1);
    fail1:
    return FAIL;
}
您可以在优秀的Regehr博客上阅读更多关于这种方法的内容:还可以指向经常使用这种模式的Linux内核本身

箭头代码

这是另一种可能的方法之一。上面的示例与“箭头”模式类似:

模式得名的一个明显问题是潜在的深层嵌套(代码看起来像箭头),为什么这个模式不那么受欢迎

其他方式

关于分配和释放资源的要求,我想不出多少(除了您在问题中详述的标记变量)。当您没有此类约束时,您会有更多的自由,您可以在其他问题和答案中看到一些很好的方法


如果资源彼此不依赖,您也可以使用其他模式(例如第一个代码示例提供的早期返回),但是如果您需要,这两种模式可以正确处理资源依赖性(即,资源2只能在有效资源1的基础上分配),如果给定资源的空闲函数没有处理失败的分配返回。

这是极少数情况下,
goto
非常有用的情况之一。只是脱离主题-不要
free
x
y
它们不是分配内存的指针。抱歉!想键入pBufX和pBufY。我的错。忽略all模糊处理的变体,比如你的第二个例子,这里只有3种非条件分支的合理方式,你可以使用:从函数返回(丑陋)、转到失败(丑陋)和循环+中断(丑陋)。我选择最不丑陋的。我明白了。忘了你可以用泛型free()释放空指针。我们的代码所使用的内存管理器中不允许使用。虽然此解决方案还可以(尽管它是80年代基础编程中的设计模式:)),我相信从函数返回会更干净。理想情况下,如果可能的话,您可以将内存分配从实际算法中分离出来,以便调用者处理所有问题allocation@nushydude如果您的库不允许使用免费(NUL)
(顺便说一句,这绝对是非标准的),然后您可以将此行为封装在一个忽略空指针的
myfree
函数中。@Lundin是的,我相信该函数可以设计为永远不会出现此问题。遗憾的是,这是针对新项目的。如果您提前进行分配,您可以使用
int*a=malloc(sizeof(*a)*x);int*b=malloc sizeof(*b)*y)
而不是显式地设置为null然后赋值。编译器可能会这样做,所以这并不重要。我同意重构评论。遗憾的是,我习惯了我们使用的内存管理器的操作方式。它无法释放null指针(给出断言)所以我们必须在释放之前检查空指针,因此我们总是将它们初始化为空,然后开始我们的工作。我刚刚为这个例子编写了malloc和free。是的,编译器应该对它们进行优化。只是让我们的代码更长。
inline bool doStuff (int x, int y, int* pBufX, int* pBufy)

bool myFunc (int x, int y)
{
  bool result;
  int *pBufX = NULL;
  int *pBufY = NULL;

  /* do allocations here if possible */

  result = doStuff(x, y, pBufX, pBufY); // actual algorithm

  free(pBufX);
  free(pBufY);

  return result;
}
int function(void){
    res_type_1 *resource1;
    res_type_2 *resource2;

    resource1 = allocate_res_type_1();
    if (resource1 == NULL){ goto fail1; }
    resource2 = allocate_res_type_2();
    if (resource2 == NULL){ goto fail2; }

    /* Main logic, may have failure exits to fail3 */

    return SUCCESS;

    fail3:
    free_res_type_2(resource2);
    fail2:
    free_res_type_1(resource1);
    fail1:
    return FAIL;
}
int function(void){
    res_type_1 *resource1;
    res_type_2 *resource2;
    int        ret = FAIL;

    resource1 = allocate_res_type_1();
    if (resource1 != NULL){
        resource2 = allocate_res_type_2();
        if (resource2 != NULL){

            /* Main logic, should set ret = SUCCESS; if succeeds */

            free_res_type_2(resource2);
        }
        free_res_type_1(resource1);
    }

    return ret;
}