在C或C ++好的gotos的例子
在这个线程中,我们看一下在C或者C ++中使用goto
例子。 它的灵感来自人们投票决定的答案 ,因为他们以为我在开玩笑。
总结(标签从原来的变化更明确):
infinite_loop: // code goes here goto infinite_loop;
为什么比替代品更好:
- 这是具体的。
goto
是导致无条件分支的语言结构。 替代scheme取决于使用支持条件分支的结构,具有退化的始终为真的条件。 - 标签文件的意图没有额外的意见。
- 读者不必扫描早期
break
的干预代码(尽pipe对于一个无原则的黑客来说仍然有可能模拟continue
早期goto
)。
规则:
- 假装gotophobes没有赢。 据了解,上述不能用于实际的代码,因为它违背成熟的习惯用法。
- 假设我们都听说过“后藤认为有害”,并知道goto可以用来写意大利面代码。
- 如果你不同意这样一个例子,就单凭技术上的优点来批评它(“因为人们不喜欢转职”不是技术上的原因)。
让我们看看我们是否可以像大人一样谈论这个问题。
编辑
这个问题现在似乎已经完成 它产生了一些高质量的答案。 感谢大家,尤其是那些认真对待我的小循环例子的人。 大多数怀疑者担心缺乏范围。 正如@quinmars在评论中指出的,你总是可以在循环体上加上大括号。 我顺便注意到for(;;)
和while(true)
不要给你任意的花括号(并且省略它们会导致烦人的错误)。 无论如何,我不会再为了这个小事而浪费你的大脑力量 – 我可以忍受for(;;)
和while(true)
(如果我想保住我的工作)那样的无害和习惯。
考虑到其他的反应,我发现很多人把goto
看成是你必须以另一种方式重写的东西。 当然,你可以通过引入一个循环,一个额外的标志,一堆嵌套的if
或其他东西来避免goto
,但是为什么不考虑goto
是否是最好的工具呢? 换句话说,人们为了达到预期的目的而准备忍受多less丑陋,以避免使用内置的语言特征? 我的意见是,即使添加一个标志是太高的价格付出。 我喜欢我的variables来表示问题或解决scheme域中的东西。 “单纯为了避免转向”不会削减它。
我会接受第一个给C模式分支到清理块的答案。 国际海事组织,这是所有张贴答案goto
最强大的情况下,当然,如果你衡量一个憎恨必须经历,以避免它的扭曲。
这里有一个我听说过的人使用的技巧。 我从来没有在野外看到它。 它只适用于C,因为C ++有RAII更通俗地做这个。
void foo() { if (!doA()) goto exit; if (!doB()) goto cleanupA; if (!doC()) goto cleanupB; /* everything has succeeded */ return; cleanupB: undoB(); cleanupA: undoA(); exit: return; }
C中对GOTO的经典需求如下
for ... for ... if(breakout_condition) goto final; final:
没有一个转到跳转嵌套循环没有直接的方法。
这是我的非愚蠢的例子,(来自Stevens APITUE)用于Unix系统调用,可能被一个信号中断。
restart: if (system_call() == -1) { if (errno == EINTR) goto restart; // handle real errors }
另一种方法是退化循环。 如果系统调用被信号中断,则重新启动它。
如果达夫的设备不需要转到,那么你也不应该! ;)
void dsend(int count) { int n; if (!count) return; n = (count + 7) / 8; switch (count % 8) { case 0: do { puts("case 0"); case 7: puts("case 7"); case 6: puts("case 6"); case 5: puts("case 5"); case 4: puts("case 4"); case 3: puts("case 3"); case 2: puts("case 2"); case 1: puts("case 1"); } while (--n > 0); } }
上面的代码来自Wikipedia 条目 。
Knuth写了一篇论文“用GOTO语句进行结构化编程”,你可以从这里得到它。 你会发现很多例子。
很常见。
do_stuff(thingy) { lock(thingy); foo; if (foo failed) { status = -EFOO; goto OUT; } bar; if (bar failed) { status = -EBAR; goto OUT; } do_stuff_to(thingy); OUT: unlock(thingy); return status; }
我使用goto
的唯一情况是跳跃前进,通常是跳出块,而不是跳转到块。 这样可以避免do{}while(0)
和其他增加嵌套的构造的滥用,同时仍然保持可读的结构化代码。
我一般都没有反对gotos,但我可以想出几个原因,为什么你不想像你提到的那样使用它们:
- 它不会限制范围,因此您在里面使用的任何临时variables将不会被释放,直到以后。
- 它不限制范围,因此可能导致错误。
- 它不会限制作用域,因此,在以后的代码中,不能在同一个作用域中重复使用相同的variables名称。
- 它不会限制范围,因此您有机会跳过variables声明。
- 人们不习惯它,它会让你的代码难以阅读。
- 这种types的嵌套循环可以导致意大利面代码,法线循环不会导致意大利面代码。
使用goto的一个好地方是可以在几个地方中止的程序,每个程序都需要各种级别的清理。 Gotophobes总是可以用结构化代码和一系列testing代替gotos,但是我认为这更直接,因为它消除了过多的缩进:
if(!openDataFile()) 离开; if(!getDataFromFile()) goto closeFileAndQuit; 如果(!allocateSomeResources) 转到freeResourcesAndQuit; //在这里做更多的工作.... freeResourcesAndQuit: //免费资源 closeFileAndQuit: //closures文件 放弃: // 放弃!
@ fizzer.myopenid.com:您发布的代码片段等同于以下内容:
while (system_call() == -1) { if (errno != EINTR) { // handle real errors break; } }
我绝对喜欢这种forms。
即使我越来越讨厌这种模式,它已经成为COM编程的基础。
#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error} ... HRESULT SomeMethod(IFoo* pFoo) { HRESULT hr = S_OK; IfFailGo( pFoo->PerformAction() ); IfFailGo( pFoo->SomeOtherAction() ); Error: return hr; }
下面是一个很好的例子:
// No Code
我已经看到使用正确,但情况是非常丑陋的。 只有当使用goto
本身比原来差得多的时候。 @Johnathon荷兰的问题是你的版本不太清楚。 人们似乎害怕局部variables:
void foo() { bool doAsuccess = doA(); bool doBsuccess = doAsuccess && doB(); bool doCsuccess = doBsuccess && doC(); if (!doCsuccess) { if (doBsuccess) undoB(); if (doAsuccess) undoA(); } }
我更喜欢这样的循环,但有些人喜欢while(true)
。
for (;;) { //code goes here }
我对此抱怨的是你失去了封锁的范围; 任何在gotos之间声明的局部variables在循环被破坏的情况下仍然有效。 (也许你假设循环是永远运行的,但我不认为这就是最初的问题作者所要求的)
C ++的范围问题更多的是一个问题,因为一些对象可能取决于他们在适当的时候被调用。
对我来说,使用goto的最好的理由是在多步初始化过程中,如果一个失败的话,所有的inits都是非常重要的,la:
if(!foo_init()) goto bye; if(!bar_init()) goto foo_bye; if(!xyzzy_init()) goto bar_bye; return TRUE; bar_bye: bar_terminate(); foo_bye: foo_terminate(); bye: return FALSE;
我不使用goto的自己,但是我曾经和一个人一起工作,在特定的情况下使用它们。 如果我没有记错的话,他的理由是围绕性能问题 – 他也有具体的规则。 总是在相同的function,并且标签总是低于goto语句。
#include <stdio.h> #include <string.h> int main() { char name[64]; char url[80]; /*The final url name with http://www..com*/ char *pName; int x; pName = name; INPUT: printf("\nWrite the name of a web page (Without www, http, .com) "); gets(name); for(x=0;x<=(strlen(name));x++) if(*(pName+0) == '\0' || *(pName+x) == ' ') { printf("Name blank or with spaces!"); getch(); system("cls"); goto INPUT; } strcpy(url,"http://www."); strcat(url,name); strcat(url,".com"); printf("%s",url); return(0); }
@格雷格:
为什么不这样做你的例子:
void foo() { if (doA()) { if (doB()) { if (!doC()) { UndoA(); UndoB(); } } else { UndoA(); } } return; }