每个人都知道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的观点,因为 他最近写道: “请不要落入。 相信我是可怕的 教条的关于[去]的教条的 声明)。我觉得很不舒服 感觉别人在做 宗教出来了,好像 编程的概念问题 只用一个小技巧就能解决,用什么 一种简单的编码纪律!”


当前回答

c++包含构造函数和析构函数。这允许一种称为RAII(资源分配是初始化)的模式。基本上,您创建一个本地堆栈变量,创建堆栈变量的行为打开一个文件,分配内存,锁定一个互斥锁,或以其他方式获取一个稍后必须释放的资源。

当变量超出作用域时,析构函数将运行并释放资源。

C语言没有这个特性。但您仍然经常需要在函数开始时获取资源,并在结束时释放它们。

你的函数可能有一个或多个错误条件导致它提前返回。您不希望重复资源释放代码。解决方案是使用goto。

例子:

int
foo(const char *arg)
{
    char *argcopy = strdup(arg);

    if (!isvalid(argcopy))
        goto out1;

    FILE *myfile = fopen(argcopy, "r");
    if (myfile == NULL)
      goto out1;

    char bytes[10];
    if (fread(bytes, sizeof(bytes), 1, myfile) != sizeof(mybytes))
        goto out2;

    /* do some actual work */
    /* .... */
    /* end of actual work */

    out2:
    fclose(myfile);

    out1:
    free(argcopy);

    return 0;
 }

其他回答

GOTO就像台锯,在采取适当的安全措施时非常有用。

我认为这是有害的,因为大多数初学者都失去了桌子锯和goto的手指。

在某些情况下,这是控制心流的唯一方法,但这些情况是可以避免的。

从这里的任何答案中我都没有看到的一件事是,“去”解决方案通常比经常提到的结构化编程解决方案更有效。

考虑多嵌套循环的情况,其中使用'goto'而不是一堆if(breakVariable)节显然更有效。“将循环放入函数并使用return”的解决方案通常是完全不合理的。在循环可能使用局部变量的情况下,现在必须通过函数参数传递它们,可能会处理由此产生的额外麻烦。

现在考虑一下清理情况,我自己经常使用这种情况,而且这种情况非常常见,可能是try{} catch{}结构在许多语言中都不可用的原因。完成相同任务所需的检查和额外变量的数量远比完成跳转所需的一两个指令要糟糕得多,而且,额外的函数解决方案根本不是解决方案。你不能告诉我这更容易管理或更易于阅读。

现在,代码空间、堆栈使用和执行时间在许多情况下对许多程序员来说可能还不够重要,但当您在一个只有2KB代码空间的嵌入式环境中工作时,为了避免一个明确定义的“goto”而额外执行50字节的指令是可笑的,而且这种情况并不像许多高级程序员所认为的那样罕见。

“goto有害”这句话对迈向结构化编程非常有帮助,即使它总是一种过度概括。在这一点上,我们都听到了足够多的使用它的谨慎(我们应该)。当它显然是工作的正确工具时,我们不需要害怕它。

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

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

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

从来都不是,只要你能独立思考。

我避免使用它,因为同事/经理无疑会在代码评审或偶然发现它时质疑它的使用。虽然我认为它有一些用途(例如错误处理案例),但你会遇到一些其他开发人员,他们会遇到一些类型的问题。

这不值得。