推荐在C中的单个函数中处理多个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)
    {
        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; 
}

这种方法的问题是,如果malloc的数量很高,您可能会忘记释放一些,从而导致内存泄漏。此外,如果出现错误时需要输出某种日志,则代码会变得非常长。

我看到过用goto处理这些问题的代码,在goto中,您只需在一个地方清除一次所有malloc。代码不长,但我不喜欢使用gotos。

有比这两种方法更好的方法吗?

也许问题出在设计上。当涉及到最小化多个malloc时,在设计函数时有经验法则吗?

编辑:我看到并使用了另一种方式。不使用goto,而是保留程序的状态,只有在状态为OK时才继续。类似于goto,但不使用goto。但这会增加if语句的数量,这可能会使代码运行速度变慢。

bool myFunc(int x, int y)
{
    int *pBufX = null;
    int *pBufY = null;
    bool bRet  = true;
    if((x <= 0) || (y <= 0))
    {
        return false;
    }
    pBufX = (int*)malloc(sizeof(int) * x);
    if(pBufX == null)
    {
       bRet = false; 
    }
    if(bRet == true)
    {
        pBufY = (int*)malloc(sizeof(int) * y);
        if(pBufY == null)
        {
            bRet = false;
        } 
    }
    //do something useful
    if(pBufX != null)
        free(pBufX);
    if(pBufY != null)    
        free(pBufY);
    return bRet; 
}

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

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

我不会再推荐你的第二个("邪恶的"后藤回避)解决方案。它比goto解决方案更复杂。

顺便说一句,释放空指针是安全的,因此

if(pBufY != NULL)     // NULL and not null
  free(pBufY);        // not y but pBufY

可替换为:

  free(pBufY);

就我个人而言,我更喜欢使用goto。但由于free(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;
}

如果你需要分配的内存来进行计算,以获得进一步分配的大小,那么你的函数可能足够复杂,无论如何都应该重构它。

好问题!(我以为我会发现它是一个骗局,但不,奇怪的是,C中如此重要的方面以前似乎从未真正问过)

我发现有两个问题涵盖了这个领域:

  • 在C中有效使用goto进行错误管理
  • 在直接C程序中,有什么好的错误处理习惯用法吗

这些主要集中在goto方式上。所以,首先让我们来介绍一下。

goto方式

如果你有一个依赖于分配几个资源的代码,这些资源必须在之后发布,你可能会使用下面的模式:

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博客上阅读更多关于这种方法的信息:http://blog.regehr.org/archives/894也指向经常使用这种模式的Linux内核本身。

箭头代码

这是另一种可能的方法。上面的例子看起来像一个"箭头"模式:

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

该模式命名的明显问题是潜在的深度嵌套(代码看起来像箭头),这就是为什么该模式不太受欢迎的原因。

其他方式

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

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

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

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中进行分配,则需要将指针传递给指针!

相关内容

  • 没有找到相关文章

最新更新