发生错误时如何释放以前分配的内存?

时间:2011-09-20 20:35:25

标签: c memory-management error-handling

给出这样的函数声明:

int base_address(zval *object, int add_prefix, char **base_address TSRMLS_DC) {    
    int result;

    char *host;
    long port;
    char *prefix;  

    host = ... get host from object ...;
    port = ... get port from object ...;
    prefix = ... get prefix from object ...;

    result = SUCCESS;

    if (asprintf(base_address, "%s:%ld/%s", host, port, prefix) < 0) {
        result = FAILURE;
    }

    return result;
}

void my_func() {
    char *base_address;
    char *ping_url;

    if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {
        MALLOC_ERROR();
    }

    if (asprintf(&ping_url, "%s/ping", base_address) < 0) {
        MALLOC_ERROR();
    }

   ... do some stuff with base address ...

    // release both, as everything worked
    free(base_address);
    free(ping_url);
}

如果第一次调用base_address成功并且第二次调用asprintf()失败,我该如何干净地跳到函数末尾才能安全地释放已分配的内存?

在没有太多代码重复或goto语句的情况下,如何在一个接一个地分配内存(并且每个分配可能失败)的情况下,是否有一些标准模式可以避免内存泄漏?

4 个答案:

答案 0 :(得分:9)

不要害怕goto。这是在C中处理异常的最简单,最清晰,最清晰的方式:

  • 你不要重复自己。重复的代码容易出错。

  • 您不会创建深层嵌套代码。深嵌套是难以辨认的。

  • 您不会隐藏在do {...} while (0)break后面。好的代码说明了它的含义。

这是一个基本的例子:

int operation() {

    int result = SUCCESS;

    if ((result = may_fail_first()) == FAILURE) {
        goto failed_first;
    }

    if ((result = may_fail_second()) == FAILURE) {
        goto failed_second;
    }

    // If your cleanup code doesn't ordinarily need to run.
    goto end;

failed_second:
    cleanup_second();

    // If you don't need to clean up everything.
    goto end;

failed_first:
    cleanup_first();

end:
    return result;

}

答案 1 :(得分:7)

这是goto用于错误处理的合理用法之一:

if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {
    goto end;
}

if (asprintf(&ping_url, "%s/ping", base_address) < 0) {
    goto release_address;
}

// do stuff

release_address:
free(base_address);
end:

这样,您就不必重复相同的发布代码,以防您有多个相互依赖的分配呼叫。

您可能想要参考我的另一个答案here,其中讨论了一般情况。

答案 2 :(得分:2)

在c中,你几乎要做if()以处理错误处理

在你的情况下

if ( base_address( ... ) )
{
  if (asprint( ... ))
  {
     /// do some stuff 

     free(ping_url);
  }
  free( base_address);
}

答案 3 :(得分:2)

如果在声明期间为指针变量分配NULL,那么free将能够处理它们从未被malloc编辑的情况(它将什么都不做)。 (例如,可以使用其他保护,例如-1来表示无效的fd。)

我将此结合使用goto进行(额外)清理 - 其他一些答案对我来说是“罗嗦”。我发现goto必须明智地使用以避免意大利面条代码,而且一般来说,我发现“周围的结果”太难以持续跟踪。首先,NULL的额外赋值还允许变量本身用作检查员(在任何可能的清理期间或其他情况下)。

快乐的编码。


示例:

void my_func() {
    char *base_address = NULL;
    char *ping_url = NULL;

    if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {
        goto cleanup;
    }

    if (asprintf(&ping_url, "%s/ping", base_address) < 0) {
        goto cleanup;
    }

    // stuff... and assign return value (or return directly) if applicable,
    // assign NULL to variables which contain values that should
    // not be free'd, etc (use as guards!).
    // I prefer to add guards into the cleanup vs. "skip over it".
    // I vary rarely, if ever, have multiple cleanup sections -- in most
    // cases this would indicate the need for additional function(s) to me.

  cleanup:
    free(base_address);
    if (ping_url) {
       // perhaps need additional cleanup        
       free(ping_url);
    }

    // Return value, if applicable. (If a "direct return" above isn't
    // advisable for the given function due to cleanup rules.)
}