每个人都知道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有害”更多的是关于状态的封装和一致性。

许多代码,甚至是'oo'代码,都有像意大利面条代码一样糟糕的混乱状态封装。

“goto有害”的问题是,它让程序员只看机制规则而不理解这样的印象,即唯一可用的流控制应该是返回方法,这很容易导致通过引用传递许多状态——这又导致缺乏状态封装,而这正是“goto有害”试图摆脱的东西。

遵循典型的“OO”代码库中的控制流,并告诉我我们仍然没有意大利面条代码....(顺便说一下,我并不是指那些经常让人讨厌的“馄饨”代码——馄饨代码的执行路径通常是非常简单的,即使对象关系不是立即明显的)。

或者,换一种说法,避免gotos而将所有东西都作为子例程,只有在每个子例程只修改局部状态时才有用,只有通过该子例程(或至少该对象)才能修改局部状态。

其他回答

在我编程生涯的早期,有一次,我编写了一个由一系列链函数组成的程序,其中每个函数在给定成功条件和完成条件的情况下调用它的后继函数。

这是一个丑陋的笨拙,有许多严重的问题,最严重的是,在它下面的所有函数都终止之前,任何函数都不能终止。

但是它很快就被开发出来了,对于它所要解决的有限的问题集工作得很好,并且明确地显示了程序的逻辑和流程,当我对它进行重构和扩展以包含在另一个项目中时,它工作得很好。

我的意见是在有意义的时候使用它,并在方便的时候尽快重构它。

以下陈述是概括;尽管抗辩例外总是可能的,但通常(以我的经验和拙见)不值得冒险。

Unconstrained use of memory addresses (either GOTO or raw pointers) provides too many opportunities to make easily avoidable mistakes. The more ways there are to arrive at a particular "location" in the code, the less confident one can be about what the state of the system is at that point. (See below.) Structured programming IMHO is less about "avoiding GOTOs" and more about making the structure of the code match the structure of the data. For example, a repeating data structure (e.g. array, sequential file, etc.) is naturally processed by a repeated unit of code. Having built-in structures (e.g. while, for, until, for-each, etc.) allows the programmer to avoid the tedium of repeating the same cliched code patterns. Even if GOTO is low-level implementation detail (not always the case!) it's below the level that the programmer should be thinking. How many programmers balance their personal checkbooks in raw binary? How many programmers worry about which sector on the disk contains a particular record, instead of just providing a key to a database engine (and how many ways could things go wrong if we really wrote all of our programs in terms of physical disk sectors)?

以上附注:

关于第2点,考虑以下代码:

    a = b + 1
    /* do something with a */

在代码中的“do something”点,我们可以高度自信地声明a大于b。(是的,我忽略了未捕获整数溢出的可能性。我们不要拘泥于一个简单的例子。)

另一方面,如果代码是这样读的:

    ...
    goto 10
    ...
    a = b + 1
    10: /* do something with a */
    ...
    goto 10
    ...

标记10的方法的多样性意味着我们必须更加努力才能确信a和b在这一点上的关系。(事实上,在一般情况下,这是不可判断的!)

关于第4点,代码中“去某个地方”的整个概念只是一个比喻。除了电子和光子(用于余热),CPU内部没有任何东西真正“去”到任何地方。有时候,我们会放弃一个比喻,转而使用另一个更有用的比喻。我记得(几十年前!)遇到过一种语言

    if (some condition) {
      action-1
    } else {
      action-2
    }

通过将action-1和action-2编译为行外无参数例程,然后使用单个双参数VM操作码(使用条件的布尔值来调用其中一个)在虚拟机上实现。这个概念只是“选择现在调用什么”,而不是“去这里或去那里”。再一次,换一个比喻。

我只需要它在基本(即。VB, VBScript等)和批处理文件。然后我只将它用于错误处理。在Basic中,我倾向于只使用“on error goto”。在批处理文件中,我必须使用它,因为没有其他命令。然后,我只使用它们作为向前跳转到有意义的标签。

在生成C状态机时,使用GOTO会很好。我永远不会在手写代码中使用GOTO——“现代”语言结构使它完全没有必要。

setjmp/longjmp构造在某些情况下(缺少“真正的”异常时,或者在实现Chicken scheme之类的东西时)可能有用,但在“普通”编程中没有它的位置。

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