每个人都知道Dijkstra的《致编辑的信》:goto语句被认为是有害的(这里。html transcript和这里。pdf),从那时起,就有一种强大的推动力,尽可能避免使用goto语句。虽然可以使用goto来生成不可维护的、庞大的代码,但它仍然存在于现代编程语言中。即使Scheme中先进的连续控制结构也可以被描述为复杂的后向。

在什么情况下需要使用goto?什么时候最好避免?

作为一个后续问题:C提供了一对函数setjmp()和longjmp(),它们不仅提供了在当前堆栈帧内进行跳转的能力,还提供了在任何调用帧内进行跳转的能力。这些应该被认为和goto一样危险吗?更危险?


Dijkstra自己也对这个头衔感到后悔,因为这不是他的责任。在EWD1308的结尾(也在这里。pdf),他写道:

Finally a short story for the record. In 1968, the Communications of the ACM published a text of mine under the title "The goto statement considered harmful", which in later years would be most frequently referenced, regrettably, however, often by authors who had seen no more of it than its title, which became a cornerstone of my fame by becoming a template: we would see all sorts of articles under the title "X considered harmful" for almost any X, including one titled "Dijkstra considered harmful". But what had happened? I had submitted a paper under the title "A case against the goto statement", which, in order to speed up its publication, the editor had changed into a "letter to the Editor", and in the process he had given it a new title of his own invention! The editor was Niklaus Wirth.

Donald E. Knuth写了一篇关于这个主题的经典论文,它与Dijkstra的论文相匹配,那就是结构化编程与语句。阅读既有助于重建上下文,也有助于对主题的非教条性理解。在本文中,Dijkstra对这个案例的观点被报道,并且更加强烈:

Donald E. Knuth:我相信通过呈现这样一个 事实上,我并不是不同意 Dijkstra的观点,因为 他最近写道: “请不要落入。 相信我是可怕的 教条的关于[去]的教条的 声明)。我觉得很不舒服 感觉别人在做 宗教出来了,好像 编程的概念问题 只用一个小技巧就能解决,用什么 一种简单的编码纪律!”


当前回答

在我的程序列表中,Goto只是为了它而包含的东西非常低。这并不意味着这是不可接受的。

Goto可以很好地用于状态机。循环中的switch语句(按典型重要性排序):(A)不能实际代表控制流,(b)丑陋,(c)可能效率低下,这取决于语言和编译器。因此,您最终为每个状态编写一个函数,并执行类似“return NEXT_STATE;”的操作,这甚至看起来像goto。

当然,以易于理解的方式对状态机进行编码是很困难的。然而,这些困难都与使用goto无关,也不能通过使用替代控制结构来减少。除非你的语言有一个“状态机”结构。我不喜欢。

在极少数情况下,当您的算法通过通过有限的允许转换集(goto)连接的节点(状态)序列的路径而不是通过任何更具体的控制流(循环、条件等等)来理解时,那么应该在代码中显式地说明这一点。你应该画一个漂亮的图。

Setjmp /longjmp可以很好地实现异常或类似异常的行为。虽然没有得到普遍的赞扬,但异常通常被认为是一种“有效的”控制结构。

Setjmp /longjmp比goto“更危险”,因为它们更难正确使用,更不用说理解了。

从来没有,将来也不会有 永远是,任何语言都是 写不好一点也难 代码。——唐纳德·克努特

从C中去掉goto并不会使用C编写好的代码变得更容易。事实上,这样做反而会忽略一点,即C应该能够作为一种被美化的汇编语言。

接下来是“有害的指针”,然后是“有害的鸭子类型”。那么,当他们来拿走你不安全的编程结构时,谁来保护你呢?是吗?

其他回答

在Linux:在Kernel Trap的内核代码中使用goto,有一个与Linus Torvalds和一个“新人”关于在Linux代码中使用goto的讨论。有一些非常好的观点,莱纳斯穿着平常的傲慢:)

一些段落:

莱纳斯:不,你被洗脑了 国安局的人认为尼克劳斯 Wirth其实知道自己是什么 说什么。他没有。他不 我有个该死的线索。”

-

莱纳斯:“我觉得goto很好,而且 它们通常比 大量的压痕。”

-

莱纳斯:“当然,用愚蠢的语言 就像帕斯卡,在那里标签不能 描述性的,后去可能很糟糕。”

我只记得用过一次goto。我有一系列五个嵌套计数循环,我需要能够根据某些条件从内部打破整个结构:

    for{
      for{
        for{
          for{
            for{
              if(stuff){
                GOTO ENDOFLOOPS;
              }
            }
          }
        }
      }
    }
    
    ENDOFLOOPS:

我可以很容易地声明一个布尔中断变量,并将其用作每个循环的条件的一部分,但在这种情况下,我认为GOTO是一样实用和一样可读的。

没有迅猛龙攻击我。

goto的基本思想是给你太多的自由去做你不想做的事情。它可能在与goto语句无关的地方导致错误,因此使代码维护更加困难。如果你认为你需要一个goto语句,你错了:),你应该重新思考你的代码结构。这就是为什么现代编程语言投入了大量精力来提供可读的、可维护的流控制结构和异常处理机制。

我也不同意拉塞夫克的观点。由于goto被滥用的次数多于正确使用的次数,我相信它在设计良好的语言中没有一席之地。即使对于goto的“理想”用途,其他需要更多代码的方式也应该是首选。

所以总的来说,是的,它仍然被认为是有害的。

因为goto可以用于令人困惑的元编程

Goto既是高级控件表达式,也是低级控件表达式,因此它没有适合大多数问题的合适设计模式。

它是低级的,因为goto是一个基本操作,它实现了一些高级操作,比如while或foreach之类的。

从某种意义上说,它是高级的,当以某种方式使用时,它将以一种清晰的顺序执行的代码,以一种不间断的方式(除了结构化循环),并将其转换为逻辑片段,这些逻辑片段有足够的gotos,可以动态地重新组装逻辑。

所以,有平淡的一面,也有邪恶的一面。

平淡的一面是,一个向上指向的goto可以实现一个完全合理的循环,而一个向下指向的goto可以执行一个完全合理的中断或返回。当然,实际的while、break或return语句可读性更强,因为可怜的人不需要为了了解全局而模拟goto语句的效果。总的来说,这是个坏主意。

The evil side involves a routine not using goto for while, break, or return, but using it for what's called spaghetti logic. In this case the goto-happy developer is constructing pieces of code out of a maze of goto's, and the only way to understand it is to simulate it mentally as a whole, a terribly tiring task when there are many goto's. I mean, imagine the trouble of evaluating code where the else is not precisely an inverse of the if, where nested ifs might allow in some things that were rejected by the outer if, etc, etc.

Finally, to really cover the subject, we should note that essentially all early languages except Algol initially made only single statements subject to their versions of if-then-else. So, the only way to do a conditional block was to goto around it using an inverse conditional. Insane, I know, but I've read some old specs. Remember that the first computers were programmed in binary machine code so I suppose any kind of an HLL was a lifesaver; I guess they weren't too picky about exactly what HLL features they got.

说了这么多,我曾经在我写的每个程序中都加了一个go,“只是为了惹恼那些纯粹主义者”。

是的,GOTO仍然被认为是有害的。当您发现自己处于使用GOTO可能有效的罕见情况时,您应该对自己的编程技能有足够的信心,而不需要其他人的验证。任何类似GOTO的函数允许你在GOTO允许的范围内跳得更远,都应该被认为比GOTO更危险。