每个人都知道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有一些问题。一是很难看到代码是如何流动的。因为花括号,所以更容易看到if-block,但是goto隐藏了它。此外,while和if本质上也是goto,但它们有助于解释为什么要在代码中来回跳转。有了一个固定的目标,你必须自己拼凑起来。

作为练习,试着编写一些计算斐波那契数列的代码,看看当你完成后,它有多难阅读。

如果你要处理这些代码,那么我建议你写一些单元测试,然后重写它。否则,就顺其自然吧。

尽管如此,有时出于性能原因,使用goto“可能”是合适的。

其他回答

在一个完美的世界里,我们永远不需要GOTO。然而,我们生活在一个不完美的世界。我们并没有包含我们所能想到的所有控制结构的编译器。有时我觉得使用GOTO比拼凑一个并不存在的控制结构更好。

最常见的(并不是说它很常见)是循环半结构。你总是执行第一部分,也许你执行剩下的部分,然后返回,再执行第一部分。当然,你可以在while循环中使用布尔标记来实现它,但我不喜欢这个答案,因为在我看来它不太清楚。当你看到这样的东西:

loop:
  GetSomeData;
  if GotData then
     Begin
        ProcessTheData;
        StoreTheResult;
        Goto Loop;
     End;

对我来说,这比

Repeat
  GetSomeData;
  Flag := GotData;
  if Flag then
    Begin
      ProcessTheData;
      StoreTheResult;
    End;
Until Not Flag;

有些时候

Function GotTheData;

Begin
  GetSomeData;
  Result := GotData;
End;

While GotTheData do
  Begin
    ProcessTheData;
    StoreTheResult;
  End;

不是一个可行的答案,我坚信代码应该是清晰的。如果我必须做一个注释来解释代码在做什么,我会考虑是否可以让代码更清晰,并去掉注释。

我唯一同意使用Goto的地方是当你需要处理错误时,每一个特定的错误点都需要特殊的处理。

例如,如果您正在获取资源并使用信号量或互斥,您必须按顺序获取它们,并且应该始终以相反的方式释放它们。

一些代码需要一种非常奇怪的模式来获取这些资源,并且您不能仅仅编写一个易于维护和理解的控制结构来正确处理这些资源的获取和释放以避免死锁。

在没有goto的情况下总是可以做得很好,但在这种情况下和其他一些情况下,goto实际上是更好的解决方案,主要是为了可读性和可维护性。

亚当

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

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

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

没有迅猛龙攻击我。

没有所谓的GOTO被认为是有害的。

GOTO是一种工具,和所有工具一样,它可以被使用和滥用。

然而,在编程世界中有许多工具倾向于被滥用而不是被使用,GOTO就是其中之一。Delphi的WITH语句是另一个。

就我个人而言,我在典型的代码中不使用这两种方法,但我有过GOTO和WITH的奇怪用法,这是有保证的,另一种解决方案将包含更多的代码。

最好的解决方案是编译器只警告你关键字被污染了,你必须在语句周围塞几个pragma指令来消除警告。

这就像告诉你的孩子不要拿着剪刀跑。剪刀并不坏,但使用它们可能不是保持健康的最佳方式。

跳跃的例子在Java字符串类源代码:

int firstUpper;

/* Now check if there are any characters that need to be changed. */
scan: {
    for (firstUpper = 0 ; firstUpper < count; ) {
         char c = value[offset+firstUpper];
         if ((c >= Character.MIN_HIGH_SURROGATE) &&
                 (c <= Character.MAX_HIGH_SURROGATE)) {
             int supplChar = codePointAt(firstUpper);
             if (supplChar != Character.toLowerCase(supplChar)) {
                  break scan;
             }
             firstUpper += Character.charCount(supplChar);
         } else {
             if (c != Character.toLowerCase(c)) {
                  break scan;
             }
             firstUpper++;
         }
     }
     return this;
}
[... subsequent use of firstUpper ...]

这可以用很少的开销重写,例如:

 int firstUpper = indexOfFirstUpper();
 if (firstUpper < 0) return this; 

即使在现代语言中,即使我实际上不喜欢使用gotos,但我认为它们在许多情况下是可以接受的,在像这样的低级情况下,我看起来更好(它不仅仅是退出循环)。

没有激起宗教战争的意图。