【问题标题】:How to make sure that malloc/free fopen/fclose are matching?如何确保 malloc/free fopen/fclose 匹配?
【发布时间】:2011-06-14 03:33:28
【问题描述】:

我觉得下面的代码是正常的(和malloc/free类似):

int foo(){

   FILE *fp = fopen("test.in", "r");

   int i;   

   for(i = 0; i < NUM; i ++){
       if(Match(fp, i)){    
            fclose(fp);
            return i;
       }
   }

   fclose(fp);
   return 0;
}

我们可以看到fclose(fp) 在代码中出现了两次。如果函数 foo 中有其他返回语句,它会出现更多。但是,麻烦的是我要多次写fclose(fp)。一种解决方案是一种功能的一种回报。但是,多次返回有时很有用。还有其他解决办法吗?

PS:据我所知,lisp 中有一个宏(with-open-file)。

(with-open-file (stream-var open-argument*) 
    body-form*)

它可以为我们打开和关闭文件。

【问题讨论】:

    标签: c fopen


    【解决方案1】:

    break 的使用通常会有所帮助:

    int foo() {
    
       FILE *fp = fopen("test.in", "r");
    
       int i, result;   
    
       result = 0;
       for(i = 0; i < NUM; i ++){
           if(Match(fp, i)){    
                result = i;
                break;
           }
       }
    
       fclose(fp);
       return result;
    }
    

    【讨论】:

    • 这是实现它的方法 - 使用变量来存储您的状态,并相应地指导您的程序执行。
    • 是的,它有效。如果代码有两个循环,就会显得丑陋,因为我应该设置另一个状态来指示内部循环终止。
    • 好吧,不一定:for (i=0; i&lt;NUMI; i++) { for (j=0; j&lt;NUMJ; j++) { if (Match(fp,i,j)) { result = i; break; } } if (result) break; }
    • 适用于此代码。如果不知道result 的值会怎样?
    • 好吧,如果你不能做一个简单的测试,那么是的,你需要添加状态变量或使用 goto。
    【解决方案2】:

    在 linux 内核的源代码中,有许多函数必须在返回时处理锁和其他资源。他们通常会在函数末尾添加一个清理标签,并在提早返回时添加goto。我个人推荐这种用法以避免重复代码,也许这是goto的唯一合理用法。

    【讨论】:

    • 我觉得函数很大的时候用goto会更好。
    • 我认为最好不要把函数做得很大。
    • @Bo Persson:确实如此。保持函数小于屏幕大小确实很有帮助。
    【解决方案3】:

    额外的间接层可以确保您不会错过函数的退出:

    int foo(FILE *fp)
    {
    
       int i;   
    
       for(i = 0; i < NUM; i ++){
           if(Match(fp, i)){    
                return i;
           }
       }
    
       return 0;
    }
    
    int foo_wrapper(void)
    {
        FILE *fp = fopen("test.in", "r");
        int out = foo(fp);
        fclose(fp);
        return out;
    }
    

    【讨论】:

    • 是的,只要给他们有意义的名字,你就可以了
    【解决方案4】:

    我将通过 goto 扩展异常处理(在@Charles Peng 的回答中提到):

    你可以这样做:

    int func(...)
    {
        ...
        f = fopen(...);
        if (f == NULL) {
                log("failed opening blah blah...");
                goto err;
        }
        ...
        m = malloc(...)
        if (m == NULL) {
                log("blah blah...");
                goto err_close_f;
        }
        ...
        if (do_something(...) < 0) {
                log("blah blah...");
                goto err_close_f;
        }
        ...
        r = alloc_resource(...)
        if (r == 0) {
                log("blah blah...");
                goto err_free_m;
        }
        ...
        return 0;
    
    
    err_free_m:
        free(m);
    err_close_f:
        fclose(f);
    err:
        return -1;
    }
    

    这样做的好处是它非常易于维护。使用这个成语时,资源获取和释放看起来有点对称。此外,资源释放超出了主要逻辑,避免了最烦人的过度混乱。检查函数的错误处理是否正确非常简单(只需检查它是否跳转到适当的点,以及之前的标签是否释放任何获取的资源)...

    【讨论】:

    • 谢谢。许多文章说goto 应该在代码中消失,现在我认为goto 将出现在我的代码中。 :)
    • @lisper - 是的,使用正确的工具来完成这项工作。就我个人而言,我一直认为应该有另一种形式的goto,它仅限于函数范围(比如skip)。
    • @detly 你的意思是goto标签和goto应该在同一个函数中? (PS:已经在c标准中了。)
    • @lisper - 是的,总是。不过,我不知道标准中有这样的限制——我从来没有在我写过的任何东西中使用goto(尽管我在微处理器引导加载程序代码中使用了“长跳转”,但这是定义的实现无论如何行为)。
    • @lisper - 嘿,我刚刚检查过,你是对的!一直以来,我都不知道这是一个实际的限制。
    【解决方案5】:

    不在函数末尾的 return 语句等效于 goto 语句。尽管使用多个返回语句可能看起来有些函数更简单,但根据我的经验,在维护各种代码库时,每个函数只有一个退出点的代码库更容易维护。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-04
      • 2014-12-02
      • 1970-01-01
      • 1970-01-01
      • 2015-01-13
      • 2015-07-20
      • 2011-07-05
      • 1970-01-01
      相关资源
      最近更新 更多