管理C程序资源的最佳方法是什么?我应该使用嵌套的if结构还是应该使用goto语句?
我知道有很多的忌讳有关的goto语句.但是,我认为当地资源清理是合理的.我提供了两个样品.一个比较嵌套的if结构,另一个使用goto语句.我个人发现goto语句使代码更容易阅读.对于那些可能认为嵌套if提示更好的结构的人来说,想象一下如果数据类型不是char*,就像Windows句柄一样.我觉得嵌套的if结构会随着一系列CreateFile函数或任何其他需要大量参数的函数而失控.
该文章表明,本地goto语句创建C代码RAII.代码很简洁易于理解.想象一下,作为一系列嵌套的if语句.
我知道goto在许多其他语言中都是禁忌,因为它们存在其他控制机制,如try/catch等,但是,在C中似乎是合适的.
#include <stdlib.h>
#define STRING_MAX 10
void gotoExample()
{
char *string1, *string2, *string3, *string4, *string5;
if ( !(string1 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string1;
if ( !(string2 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string2;
if ( !(string3 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string3;
if ( !(string4 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string4;
if ( !(string5 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string5;
//important code goes here
gotoExample_string5:
free(string4);
gotoExample_string4:
free(string3);
gotoExample_string3:
free(string2);
gotoExample_string2:
free(string1);
gotoExample_string1:
}
void nestedIfExample()
{
char *string1, *string2, *string3, *string4, *string5;
if (string1 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string2 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string3 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string4 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string5 = (char*) calloc(STRING_MAX, sizeof(char)))
{
//important code here
free(string5);
}
free(string4);
}
free(string3);
}
free(string2);
}
free(string1);
}
}
int main(int argc, char* argv[])
{
nestedIfExample();
gotoExample();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我还要引用Linus Torvalds对Linux 内核中的goto语句的引用.
有时结构很糟糕,并且妨碍了使用"goto"更加清晰.
例如,有条件的东西是非常常见的.
在这种情况下,您有两种可能性
使用goto,并开心,因为它不强制嵌套
这使得代码更具可读性,因为代码只执行算法所说的应该做的事情.
复制代码,并以嵌套形式重写它,以便您可以
使用结构化跳转.这通常会使代码更难以阅读,更难维护和更大.
Pascal语言是后一个问题的一个主要例子.因为它没有"break"语句,所以(传统的)Pascal中的循环通常看起来像是完全糟糕,因为你必须添加完全任意的逻辑来说"我现在已经完成了".
是转到资源管理是否可以接受?我应该使用嵌套的if语句还是有更好的方法?
更新: C中Good Gotos的例子
Nic*_*kis 11
如果使用goto
你可以避免编写复杂的代码,那么使用goto
.
您的示例也可以这样写(不是goto
):
void anotherExample()
{
char *string1, *string2, *string3, *string4, *string5;
string1 = string2 = string3 = string4 = string5 = 0;
if ((string1 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string2 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string3 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string4 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string5 = (char*) calloc(STRING_MAX, sizeof(char))))
{
//important code here
}
free(string1);
free(string2);
free(string3);
free(string4);
free(string5);
}
Run Code Online (Sandbox Code Playgroud)
Nea*_*alB 11
毫无疑问,Dijkstra在编程世界中是一个令人敬畏的人物.他的 Goto被认为是有害的论文被夸大了.是GoTo可能被滥用,并且可能有害,但许多人认为对GoTo的彻底禁令是不合理的.Knuth向Dijkstra提供了一个非常合理的反驳: GO TOs的结构化编程
阅读Knuth的论文,你会发现你的GoTo模式是GoTo的一个很好的用途.
顺便说一下,Dijkstra也很适合其他一些事情.怎么样:
Dijkstra是一位伟大的数学家,为计算机科学做出了巨大贡献.但是,我不认为他必须处理或感兴趣的是我们99.99%的程序所做的日常类型的事情.
仅在原因和结构上使用GoTo.很少使用它们.但要使用它们.
nin*_*alj 10
清理使用goto
的优点是它不易出错.必须释放在每个返回点上分配的每个资源可能导致某人在进行维护工作时有一天会遗漏一些清理工作.
也就是说,我将引用Knuth的"带有goto语句的结构化编程":
我主张在某些情况下消除go,以及在其他情况下引入.
和Knuth在同一篇论文中对Dijkstra的引用:
"请不要陷入这样的陷阱,即我相信我对于声明的说法非常不合理.我有一种不舒服的感觉,就是其他人正在制造宗教信仰,好像编程的概念问题可以通过一个简单的编码规则形式的单一技巧!" [29].
那是我的理念.
说真的,在某些情况下a goto
是合理的,特别是如果它只是做一些明显的事情,比如跳转到函数底部的公共返回码.