代码重构,重构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;
}
Tags:  如何重构代码 什么是代码重构 如何做代码重构 代码重构原则 代码重构

延伸阅读

最新评论

发表评论