在这个主题中,我们看一下C或C ++中goto
的良好用法示例。它的灵感来自an answer人们投票,因为他们认为我在开玩笑。
摘要(标签从原来更改为使意图更清晰):
infinite_loop:
// code goes here
goto infinite_loop;
为什么它比替代品更好:
goto
是
语言结构导致
无条件的分支。备择方案
依赖于使用结构
支持条件分支,
堕落永远是真实的
条件。break
的干预代码
(虽然它仍然可能
无原则的黑客来模拟
continue
及早goto
)。规则:
让我们看看我们是否可以像成年人一样谈论这个问题。
修改
这个问题现在看来已经结束了。它产生了一些高质量的答案。谢谢大家,
特别是那些认真对待我的小圈子的人。大多数怀疑论者都很担心
由于缺乏块范围。正如@quinmars在评论中指出的那样,你总是可以把括号括起来
循环体。我顺便提一下,for(;;)
和while(true)
不会给你括号
免费(并省略它们可能导致烦恼的错误)。无论如何,我不会再浪费了
你的大脑力量在这个小事上 - 我可以忍受无害和惯用的for(;;)
和while(true)
(如果我想保住我的工作也是如此)。
考虑到其他回复,我发现有很多人将goto
视为您总是如此
必须以另一种方式重写。当然你可以通过引入一个循环来避免goto
,
一个额外的标志,一堆嵌套的if
或其他什么,但为什么不考虑goto
是否是goto
也许是这份工作的最佳工具?换句话说,人们准备忍受多少丑陋以避免使用内置语言功能达到预期目的?我的看法就是这样
即使添加旗帜也要付出太高的代价。我喜欢我的变量来表示事物
问题或解决方案域。 “仅仅避免goto
”并没有削减它。
我会接受第一个给出C模式分支到清理块的答案。 IMO,这肯定是所有发布的答案的{{1}}最强的情况 如果你通过扭曲测量它,仇恨者必须通过它来避免它。
答案 0 :(得分:87)
这是我听说人们使用的一个技巧。我从来没有在野外见过它。它只适用于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;
}
答案 1 :(得分:85)
C中GOTO的经典需求如下
for ...
for ...
if(breakout_condition)
goto final;
final:
没有goto,没有直接的方法可以打破嵌套循环。
答案 2 :(得分:32)
这是我的非愚蠢的例子,(来自Stevens APITUE),用于Unix系统调用,可能会被信号中断。
restart:
if (system_call() == -1) {
if (errno == EINTR) goto restart;
// handle real errors
}
替代方案是简并循环。这个版本读起来像英文“如果系统调用被信号中断,重启它”。
答案 3 :(得分:14)
如果Duff的设备不需要goto,那么你也不应该! ;)
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);
}
}
上面的代码来自维基百科entry。
答案 4 :(得分:14)
Knuth写了一篇论文“使用GOTO语句进行结构化编程”,你可以得到它,例如来自here。你会在那里找到很多例子。
答案 5 :(得分:12)
我一般都没有反对过任何东西,但是我可以想到为什么你不想像你提到的那样将它们用于循环的几个原因:
答案 6 :(得分:12)
很常见。
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)
和其他增加嵌套的构造,同时仍保持可读的结构化代码。
答案 7 :(得分:7)
使用goto的一个好地方是可以在几个点中止的程序,每个点都需要不同级别的清理。 Gotophobes总是可以用结构化代码和一系列测试替换gotos,但我认为这更简单,因为它消除了过多的缩进:
if (!openDataFile()) goto quit; if (!getDataFromFile()) goto closeFileAndQuit; if (!allocateSomeResources) goto freeResourcesAndQuit; // Do more work here.... freeResourcesAndQuit: // free resources closeFileAndQuit: // close file quit: // quit!
答案 8 :(得分:7)
@ fizzer.myopenid.com:您发布的代码段与以下内容相同:
while (system_call() == -1)
{
if (errno != EINTR)
{
// handle real errors
break;
}
}
我绝对喜欢这种形式。
答案 9 :(得分:6)
尽管随着时间的推移我已经开始讨厌这种模式,但它已经融入到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;
}
答案 10 :(得分:5)
这是一个很好的转到的例子:
// No Code
答案 11 :(得分:1)
我看过goto使用正确,但情况非常丑陋。只有当goto
本身的使用比原来的更糟糕时才会这样。
@Johnathon Holland的问题是你的版本不太清晰。人们似乎害怕局部变量:
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
}
答案 12 :(得分:0)
我对此的抱怨是你失去了区块范围;如果循环被打破,在gotos之间声明的任何局部变量仍然有效。 (也许你假设循环永远运行;但我认为这不是原始问题作者所要求的。)
范围问题更多是C ++的问题,因为某些对象可能取决于在适当的时间调用它们的dtor。
对我来说,使用goto的最佳理由是在一个多步骤的初始化过程中,如果一个失败,那么重要的是所有的内容都要退出,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;
答案 13 :(得分:0)
我自己并没有使用goto,但是我确实曾与一个曾在特定情况下使用它们的人合作过。如果我没记错的话,他的理由就是性能问题 - 他还有如何的具体规则。始终使用相同的函数,标签总是低于goto语句。
答案 14 :(得分:0)
#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);
}
答案 15 :(得分:-1)
@格雷格:
为什么不这样做你的例子:
void foo()
{
if (doA())
{
if (doB())
{
if (!doC())
{
UndoA();
UndoB();
}
}
else
{
UndoA();
}
}
return;
}