C 或 C + + 中优秀 gotos 的例子

在这个线程中,我们将查看在 C 或 C + + 中良好使用 goto的例子。它的灵感来自 一个答案,人们投票,因为他们认为我是在开玩笑。

摘要(标签由原来的更改为使意图更加清晰) :

infinite_loop:


// code goes here


goto infinite_loop;

为什么它比其他选择更好:

  • 很特别,goto是 语言结构,从而导致 无条件分行,备选方案 取决于使用的结构 支持有条件分支, 堕落的永远真实 情况。
  • 标签记录了意图 没有额外的评论。
  • 读取器不必扫描 早期 break的中间代码 (尽管仍然有可能 无原则的黑客来模拟 continue及早期 goto)。

规则:

  • 假装那些讨厌鬼不知道 赢。据了解,以上 不能在实际代码中使用,因为 它违背了成语。
  • 假设我们都听说过 “后藤被认为是有害的”并且知道 可以用来写字 意大利面条密码。
  • 如果你不同意一个例子, 从技术角度批评它 (因为人们不喜欢 Goto’不是技术原因)。

看看我们能不能像成年人一样谈谈。

剪辑

这个问题现在看来已经完成了,产生了一些高质量的答案,感谢大家, 特别是那些认真对待我的小循环例子的人。大多数怀疑论者都很担心 因为没有块作用域。正如@quinmars 在注释中指出的,您总是可以在 我注意到 for(;;)while(true)没有给大括号 无论如何,我不会再浪费了 你的脑力在这件小事上-我可以忍受无害的和惯用的 for(;;)while(true)(如果我想保住我的工作就好了)。

考虑到其他的反应,我看到很多人认为 goto是你一直认为的东西 当然你可以通过引入一个循环来避免 goto, 一个额外的标志,一堆嵌套的 if,或者其他什么,但是为什么不考虑一下 goto是否 也许是这项工作的最佳工具?换句话说,为了避免使用内置的语言特性达到预期的目的,人们准备忍受多大程度的丑陋?我认为 即使添加一个标志也是一个很高的代价。我喜欢我的变量在 问题或解决方案领域。“只是为了避免一个 goto”不削减它。

我将接受第一个答案,它给出了 C 模式,用于分支到一个清理块。在我看来,这是所有公布的答案中 goto的最强有力的证据 如果你用仇恨者为了避免这种情况而不得不经历的扭曲来衡量的话。

175548 次浏览

下面是一个好的 goto 的例子:

// No Code

我一般不反对 gotos,但我可以想出几个原因,为什么你不想使用它们像你提到的循环:

  • 它不限制范围,因此您在内部使用的任何临时变量都不会被释放,直到以后。
  • 它不限制范围,因此可能导致 bug。
  • 它不限制作用域,因此您不能在以后的代码中在同一作用域中重用相同的变量名。
  • 它不限制范围,因此您有机会跳过变量声明。
  • 人们不习惯它,它会使您的代码更难阅读。
  • 这种类型的嵌套循环可以导致意大利面条代码,法线循环不会导致意大利面条代码。

这里是我的非愚蠢的例子,(来自 Stevens APITUE) Unix 系统调用可能会被信号中断。

restart:
if (system_call() == -1) {
if (errno == EINTR) goto restart;


// handle real errors
}

另一种选择是退化循环。这个版本读起来像英语“如果系统调用被信号中断,重新启动它”。

如果 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);
}
}

以上代码来自 Wikipedia 进入

这是我听说过的人们使用的一个技巧。但我从没在野外见过。而且它只适用于 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;
}

使用 goto 的一个好地方是在一个可以在多个点中止的过程中,每个点都需要不同级别的清理。Gotophoes 总是可以用结构化代码和一系列测试来替代 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!

我对此感到不满的是,您失去了块作用域; 如果循环被打破,在 gotos 之间声明的任何局部变量都将保持有效。(也许您假设循环永远运行; 但我不认为最初的问题编写者问的是这个问题。)

范围界定的问题在 C + + 中更为严重,因为有些对象可能依赖于在适当的时候调用它们的 dtor。

对我来说,使用 goto 最好的理由是在一个多步骤初始化过程中,如果一个初始化过程失败了,那么所有的 inits 都会被备份,这一点非常重要:

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;

@ Greg:

为什么不这样做你的例子:

void foo()
{
if (doA())
{
if (doB())
{
if (!doC())
{
UndoA();
UndoB();
}
}
else
{
UndoA();
}
}
return;
}

@ fizzer. myopenid.com: 您发布的代码片段相当于以下内容:

    while (system_call() == -1)
{
if (errno != EINTR)
{
// handle real errors


break;
}
}

我当然更喜欢这种形式。

很常见。

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)和其他结构的滥用,这些结构增加了嵌套,同时仍然维护可读的结构化代码。

在 C 中对 GOTO 的典型需求如下

for ...
for ...
if(breakout_condition)
goto final;


final:

在没有 goto 的情况下,没有直接的方法可以打破嵌套循环。

尽管随着时间的推移,我越来越讨厌这种模式,但它已经深入到 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;
}

我自己不使用 goto’s,但是我曾经和一个人合作过,他会在特定情况下使用 goto’s。如果我没记错的话,他的理论基础是性能问题——他还为 怎么做制定了具体的规则。总是在同一个函数中,并且标签总是低于 goto 语句。

Knuth 已经写了一篇论文“带有 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
}
#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);
}