重构C资源释放代码

今天我在维护一个C项目的时候发现有的函数内部有很多的return语句,每个return前面都有一段相同的资源释放的代码。代码看起来就像这样:

int f() {

    char *p1 = (char*)malloc(1024 * sizeof(char));

    int *p2 = (int*)malloc(10 * sizeof(int));

    ...

    if (...) {

        free(p1);

        free(p2);

        return 1;

    }

    for (...) {

        if (...) {

            free(p1);

            free(p2);

            return 2;  

        }

    }

    ...

    free(p1);

    free(p2);

    return 0;

}

 

上面的代码资源释放部分重复度太高,读起来不够清爽。更重要的是如果某一个return之前忘记了释放资源就会发生内存泄露,只能靠程序的作者和维护者随时提高警惕。这类问题在C++中可以通过RAII来很好地解决,但可惜C语言没有自动析构机制,所以,我们需要通过一些技巧来达到类似的效果。看下面的代码:

 

int void f() {

    int return_code = 0;

    char *p1 = (char*)malloc(1024 * sizeof(char));

    int *p2 = (int*)malloc(10 * sizeof(int));

    ...

    if (...) {

        return_code = 1;

        goto _END_F;

    }

    for (...) {

        if (...) {

            return_code = 2; 

            goto _END_F;

        }

    }

    ...

_END_F:

    free(p1);

    free(p2);

    return return_code;

}

 

我们把资源释放代码统一放在函数末尾,并打上_END_F标签,把原来return的地方用goto _END_F替换。我们通过这种方式消除了资源释放部分的冗余代码,也一定程度上减小了忘记释放资源的风险。不过,由于现在需要每次在goto前面加上return_code=x,还是有一定的冗余和遗忘的危险。于是,我们可以通过下面的宏可以进一步完善:

#define RETURN(label, returnCode) { return_code = returnCode; goto label;}

这样我们就可以用RETURN(_END_F, 2)来一并实现设置返回值和goto的效果了,即简化了代码,又防止忘记设置返回值。 最终重构效果如下:

 

#define RETURN(label, returnCode) { return_code = returnCode; goto label;}

int void f() {

    int return_code = 0;

    char *p1 = (char*)malloc(1024 * sizeof(char));

    int *p2 = (int*)malloc(10 * sizeof(int));

    ...

    if (...) {

        RETURN(_END_F, 1);

    }

    for (...) {

        if (...) {

            RETURN(_END_F, 2);
        }

    }

    ...

_END_F:

    free(p1);

    free(p2);

    return return_code;

}

posted on 2011-05-25 22:38  Todd Wei  阅读(2477)  评论(4)    收藏  举报