如何确保malloc / free fopen / fclose匹配?

时间:2011-06-14 03:33:29

标签: c fopen

我认为以下代码是正常的(并且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中有其他return语句,它会显得更多。但是,我必须多次写fclose(fp)很麻烦。一种解决方案只是一个函数的一个返回。但是,多次返回有时是有用的。还有其他解决方案吗?

PS:据我所知,lisp中有一个宏(带有打开文件)。

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

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

5 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:6)

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

答案 2 :(得分: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;
}

答案 3 :(得分:1)

我将通过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;
}

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

答案 4 :(得分:1)

不在函数末尾的return语句相当于goto语句。尽管某些函数看起来好像有多个return语句,但是在保持各种代码库的同时,我的经验是每个函数只有1个退出点的代码库更容易维护。