【问题标题】:How to free previously allocated memory when errors occured?发生错误时如何释放先前分配的内存?
【发布时间】:2011-11-21 10:19:17
【问题描述】:

给定这样的函数声明:

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 语句的情况下,是否有一些标准模式如何避免内存泄漏?

【问题讨论】:

  • goto 是你的朋友
  • goto 有什么问题?您可以使用 do while(0) 构造,但这本质上是一回事。
  • @Dani:这不是 C++,所以 RAII 在这里要困难得多。 :-(
  • @PlatinumAzure:该死,有些人还活在石器时代。

标签: c memory-management error-handling


【解决方案1】:

在 c 中,你几乎可以通过 if () 来处理错误

在你的情况下

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

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

【讨论】:

    【解决方案2】:

    这是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,它谈到了一般情况。

    【讨论】:

    • 如果有人因为 goto 而投反对票,他可以直接 goto 做一些非编程的事情。
    【解决方案3】:

    不要害怕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;
    
    }
    

    【讨论】:

      【解决方案4】:

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

      我将这个结合使用,并使用goto 进行(额外的)清理——其他一些答案对我来说看起来很“罗嗦”。我发现必须明智地使用goto 以避免意大利面条代码,而且一般来说,我发现“gotos around gotos”太难以持续跟踪。首先对 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.)
      }
      

      【讨论】:

        猜你喜欢
        • 2019-01-08
        • 2017-03-22
        • 2012-04-28
        • 2013-08-07
        • 2016-11-03
        • 2021-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多