在C

时间:2015-12-09 06:43:04

标签: 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)
    {
        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处理这些代码的代码,在那里你只清除一个地方的所有mallocs。代码不长,但我不喜欢使用gotos。

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

也许问题在于设计首先。在设计函数时,最小化多个mallocs是否有经验法则?

编辑: 还有另一种我见过和使用过的方式。您不保留使用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; 
}

4 个答案:

答案 0 :(得分:4)

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

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

我不建议你的第二个(&#34;邪恶&#34; goto避免)解决方案。 goto解决方案更复杂。

BTW释放空指针是安全的,因此

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

可以替换为:

  free(pBufY);

答案 1 :(得分:1)

就个人而言,我更喜欢使用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;
}

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

答案 2 :(得分:1)

好问题! (我以为我会找到它的一个骗局,但不,很奇怪,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博客上阅读有关此方法的更多信息: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;
}

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

其他方式

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

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

答案 3 :(得分:0)

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

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,从而将内存分配与实际算法分开,但有时这是不可能的

请注意,免费保证在您向其传递空指针时它什么都不做。

编辑:

请注意,如果在doStuff内进行分配,则需要传递指针指针!