当前位置:  开发笔记 > 编程语言 > 正文

GOTO仍被视为有害吗?

如何解决《GOTO仍被视为有害吗?》经验,为你挑选了25个好方法。

每个人都知道Dijkstra 给编辑的信件:转到被认为有害的声明(也就是这里 .html脚本和这里的 .pdf)并且从那时起就有一个强大的推动,尽可能避开goto声明.虽然可以使用goto来生成不可维护的,庞大的代码,但它仍然保留在现代编程语言中.甚至Scheme中的高级连续控制结构也可以被描述为复杂的goto.

什么情况下可以使用goto?什么时候最好避免?

作为后续问题:C提供了一对函数setjmp和longjmp,它们不仅可以在当前堆栈帧内,而且可以在任何调用帧中进行转换.这些应该被视为像goto一样危险吗?更危险吗?


Dijkstra本人对这个头衔表示遗憾,他对此不负责任.在EWD1308(也是这里 .pdf)结束时,他写道:

最后是一个短篇小说的记录.1968年,ACM的通讯以" 被认为有害的goto声明 "的标题发表了我的一篇文章,遗憾的是,在后来的几年中,这一期刊最常被引用,但是,经常被作者看到的不多于标题,通过成为一个模板成为我的名声的基石:我们会看到几乎任何X的标题"X被认为有害"的各种文章,包括一个标题为"Dijkstra被认为有害"的文章.但是发生了什么?我提交了一份题为" 反对goto声明的案件 "的论文"为了加快出版速度,编辑已经变成了一封"给编辑的信",并在此过程中给了他一个新的自己的发明名称!编辑是Niklaus Wirth.

关于这个主题的经过深思熟虑的经典论文,与Dijkstra的相关,是结构化编程,由Donald E. Knuth撰写.阅读都有助于重新建立背景和对主题的非教条性理解.在本文中,Dijkstra对此案的观点得到了报道,甚至更为强烈:

Donald E. Knuth:我相信通过提出这样一种观点,我实际上并不同意Dijkstra的观点,因为他最近写了以下内容:"请不要陷入相信我非常悲惨的陷阱.去发言.我有其他人正在一个宗教出来的不舒服的感觉,就好像编程的概念问题可以通过一个单一的技巧来解决,通过编码规则的简单形式! "

Jim McKeeth.. 240

XKCD的GOTO漫画

我的一位同事说,使用GOTO的唯一原因是如果你把自己编程到一个角落,这是唯一的出路.换句话说,提前进行适当的设计,以后您不需要使用GOTO.

我认为这部漫画很精彩地说明了"我可以重组程序的流程,或者使用一点'GOTO'代替." 当你的设计薄弱时,GOTO是一种微弱的出路. 迅猛龙捕食弱者.



1> Jim McKeeth..:

XKCD的GOTO漫画

我的一位同事说,使用GOTO的唯一原因是如果你把自己编程到一个角落,这是唯一的出路.换句话说,提前进行适当的设计,以后您不需要使用GOTO.

我认为这部漫画很精彩地说明了"我可以重组程序的流程,或者使用一点'GOTO'代替." 当你的设计薄弱时,GOTO是一种微弱的出路. 迅猛龙捕食弱者.


GOTO可以从一个任意点跳到另一个任意点.迅猛龙从无处跳到这里!
你的同事错了,显然没有读过Knuth的论文.
我根本不觉得这个笑话很有趣,因为每个人都知道你必须先链接才能执行.
多年来我一直没有看到混淆和其他扭曲代码的结束,只是为了避免goto.@jimmcKeeth,上面的xkcd漫画并没有证明goto很弱.它正在取笑使用它的歇斯底里.
您确实意识到Delphi库源代码包含goto语句.这些都是goto的合适用法.
@JimMcKeeth不是这样.使用goto更容易理解一些算法.很少有例子,但也有一些例子.在RTL中,我们在`System`,`System.Generics.Defaults`和`Vcl.Imaging.GIFImg`中有例子.你可以问你的同事他们为什么在那里.您可以在GR32和Synopse库中找到示例.这是由熟练的开发人员编写的代码.没有我的代码库.但我不认为人们需要如此教条.

2> joel.neely..:

以下陈述是概括; 虽然总是可以恳求例外,但通常(根据我的经验和拙见)并不值得冒这个风险.

    无限制地使用内存地址(GOTO或原始指针)提供了太多机会来轻松避免错误.

    到达代码中特定"位置"的方式越多,对该系统状态的信心就越不自信.(见下文.)

    结构化编程IMHO不是关于"避免GOTO",而是关于使代码结构与数据结构相匹配.例如,重复数据结构(例如,阵列,顺序文件等)由重复的代码单元自然地处理.具有内置结构(例如,while,for,until,for-each等)允许程序员避免重复相同的陈词滥调代码模式的乏味.

    即使GOTO是低级实现细节(并非总是如此!),它低于程序员应该考虑的级别.有多少程序员在原始二进制文件中平衡他们的个人支票簿?有多少程序员担心磁盘上的哪个扇区包含特定记录,而不是仅仅为数据库引擎提供密钥(如果我们真的按照物理磁盘扇区编写了所有程序,那么有多少种方法会出错?)

以上脚注:

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

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

在代码中的"做某事"点,我们可以高度自信地说明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编译为out-of-line无参数例程,然后使用单个双参数VM操作码,使用条件的布尔值来调用其中一个,从而在虚拟机上实现.这个概念只是"选择现在要调用的东西",而不是"去这里或去那里".再一次,只是一个隐喻的变化.


好点.在更高级的语言中,goto甚至没有任何意义(考虑在Java中的方法之间跳转).Haskell函数可以由单个表达式组成; 试着用goto跳出来!

3> Rob Walker..:

有时在单个函数中使用GOTO替代异常处理是有效的:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

COM代码似乎经常陷入这种模式.


@Bob:如果正在清理局部变量,很难将err_cleanup代码移动到子程序中.
我同意,有合法的用例,goto可以简化代码并使其更易读/可维护,但似乎有某种goto-phobia浮动...
@ user4891惯用的C++方式不是try {} catch(){cleanup; 但是,RAII,需要清理的资源是在析构函数中完成的.每个构造函数/析构函数都只管理一个资源.
有两种方法可以在C中写这个没有goto; 两者都短得多.要么:if(f())if(g())if(h())返回成功; 清理(​​); 回归失败; 或:if(f()&& g()&& h())返回成功; 清理(​​); 回归失败;
实际上,我在COM/VB6中使用它只是因为我没有*替代,不是因为它是另一种选择.我现在对try/catch/finally感到高兴.
顺便说一下,比较布尔值也是邪恶的。

4> shsteimer..:

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

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

ENDOFLOOPS:

我可以轻松地声明一个布尔中断变量并将其用作每个循环的条件的一部分,但在这个实例中我决定GOTO同样实用且同样可读.

没有迅猛龙攻击我.


"将它重构为一个函数并将goto替换为return :)",区别在于?真的有什么区别?是不是也回去了?返回还会像goto那样制动结构化流程,在这种情况下,它们以相同的方式执行(即使goto可以用于更加简单的事情)
嵌套很多循环通常都是代码味道所有它自己.除非您正在进行,例如,5维数组乘法,否则很难想象某些内部循环无法有效地提取到较小的函数中的情况.像所有的经验法则一样,我想有一些例外.
@leppie:出于同样的原因,反对"goto"并给我们结构化编程的一代也拒绝早期回归.它归结为代码的可读性,表达程序员意图的清晰程度.除了避免使用恶意关键词之外,创建一个除了使用恶意关键词之外的其他目的的功能:治愈比疾病更糟糕.
@ButtleButkus:坦率地说,即使不是更糟,也是如此糟糕.至少使用`goto`,可以*明确地*指定目标.使用`break 5;`,(1)我必须计算循环关闭以找到目的地; (2)如果循环结构发生变化,可能需要更改该数字以保持目的地正确.如果我要避免使用`goto`,那么增益应该是不必手动跟踪那样的东西.
只有在使用支持返回的语言时,才能使用返回替换它.
如果你需要超过3个级别的缩进,你仍然会被搞砸,并且应该修复你的程序. - Linus Torvalds
如果你有一个方法"christmasTreeOfForLoops()"当你想在外面打破,你只需返回.这相当于外循环级别的中断.
嘿,如果五个循环中的每个循环只使用一次并且它们都适合小区域(不是1000行),那么重构就是浪费时间.
"将其重构为函数"与SOLE避免goto的目的是荒谬的.目标是使代码更易于维护,如果goto用于此目的,就像海报的答案一样,那么**你只是不会将代码隐藏到函数中而不必要**.
@stivlo"640K对任何人都应该足够了." - 比尔盖茨
@PCLuddite回应关于缩进的Torvalds报价.关键是你不能用权威的诉求来谴责某些东西,也不能假设一种尺寸适合所有人.有时人们使用三层以上的嵌套,有时人们使用超过640K,有时人们使用GOTO.

5> Konrad Rudol..:

我们已经讨论了这个问题,我坚持自己的观点.

此外,我讨厌大家介绍"作为更高级别的语言结构goto的化身",因为他们显然还没有得到一点在所有.例如:

甚至Scheme中的高级连续控制结构也可以被描述为复杂的goto.

这完全是胡说八道.每个控制结构都可以实现,goto但这种观察是完全无关紧要的.goto由于其积极影响而被认为是有害的,但由于其负面影响,这些已被结构化编程所消除.

同样地,说"GOTO是一种工具,并且作为所有工具,它可以被使用和滥用"完全不合适.没有现代建筑工人会使用岩石并声称它"是一种工具."岩石已被锤子取代.goto已被控制结构所取代.如果建筑工人在没有锤子的情况下被困在野外,当然他会使用岩石代替.如果程序员必须使用没有特征X的劣质编程语言,那么当然她可能不得不使用goto.但如果她在其他任何地方使用它而不是相应的语言功能,她显然不能正确理解语言并错误地使用它.它真的很简单.


当然,正确使用岩石不是锤子.其正确用途之一是磨石,或用于磨削其他工具.即使是低矮的岩石,如果使用得当也是一个很好的工具.你只需找到正确的用法.goto也是如此.
我很沮丧,很多人赞成这篇文章.您的帖子似乎只是有效,因为您从不打扰质疑您实际执行的逻辑,因此您没有注意到您的谬误.请允许我解释一下你的整个帖子:"在任何情况下都有一个优秀的goto工具,所以不应该使用gotos." 这是一个逻辑上的双条件,因此你的整个帖子基本上都是在乞求一个问题:"你怎么知道在每种情况下都有一个优秀的工具?"
那么Goto的正确使用是什么?对于每种可以想象的情况,还有另一种更适合的工具.即使你的磨石实际上被现在的高科技工具所取代,即使它们仍然是由*摇滚制成的.原材料和工具之间存在很大差异.
@Coding:不,你完全错过了帖子的要点.这是一个*riposte*而不是一个孤立的,完整的论点.我只是在主题"for""goto"中指出了这个谬论.你是对的,因为我不提出反对`goto`本身的论据 - 我不打算 - 所以毫无疑问 - 乞讨.
@jalf:Goto肯定会*存在于C#中.见http://stackoverflow.com/questions/359436/c-equivalent-to-javas-continue-label#359449
作为一个注释,我用岩石作为磨刀石.当你需要磨刀并且没有装石头时工作得很好.每种工具都有时间和地点.甚至有时候你可能会用一块石头把东西推到地上(比如设置一个帐篷,你不觉得自己要拉锤子.)永远不要说永远......还有时候使用最新的工具不是正确的选择.我已经看到人们需要花20分钟来设置一个动力锯,以便用手锯切割不到一分钟的东西.
至于你的解释,如果你仔细阅读,你会发现你只是解释了我的最后一句话.在这里,你的释义确实是正确的.但有责任在*你*上演示`goto`的使用,而不是我反驳所有可以想象的用法.那是罗素的茶壶....
循环/开关模式如何更好地模拟它们?您只是在不提高可读性的情况下添加逻辑步骤.尽管Knuth的许多观点已经过时(例如,赞同现代编程已经开发出专门技术的一般结果),但它们并非都不适用; 以文本扫描为例,例如他的例子4.
(续)请注意,我*在我的帖子中没有适当的语言特征(例如C语言中的错误处理)的语言中承认`goto`的用途,因此是岛上的摇滚类比.但是这种观察是完全无足轻重的,并不值得讨论:"鉴于不够先进的工具,我必须使用劣质工具."这种说法与重言式有关. - 总而言之,"goto"的论据是特定于语言的,不应该一概而论:Java(比如说)不需要`goto`,至少没有人能够最终证明它是有利的.
在FSM上:goto跳转到标签.循环/开关设置一个标志,跳过开关的末端,跳回到循环的顶部,测试(true),测试标志直到它到达正确的情况,然后跳转到描述性较小的标签 - 案例声明.我不明白它是如何优于goto版本的.您只需依靠编译器来优化您引入的逻辑低效率,但您仍然可以使用更多的程序来跟踪(如交换机检查的变量)和带读者的阅读器,以便了解有限状态机.
什么是有限状态机?另外,我认为你没有读过OP提到的Donald E. Knuth的文章"结构化编程与去声明"吗?
FSM并没有真正受益于`goto`s.例如,带有状态变量的循环/开关模式以相同的成本对它们建模得更好(我不同意Steve Jessop的这一点).Tribble的结束示例,LR解析器可以使用相互尾递归函数同样优雅地实现.是的,我已经阅读了Knuth对这个问题的看法,但它已经完全过时了,尽管那是一篇非常好的文章.例如,它做出了非常特定于硬件的假设(内存访问计数,即缓存)和编译器优化(循环展开).
@Tim:我知道这篇论文,我发现它没有吸引力.它缺乏实质性,支持Dijstra的论点并倾向于拆除稻草人.特别是,我的上述(简短)发布完全取消了它.此外,最后的示例忽略了解决该问题的现有最佳实践.抱歉
你击中了要害.用锤子.
顺便说一句,我们可能有责任证明goto的使用,但是你有责任反驳每一个演示用途.

6> Steve Jessop..:

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

Goto可以用于状态机.循环中的switch语句(按典型重要性顺序):( a)实际上不代表控制流,(b)丑陋,(c)取决于语言和编译器,可能效率低下.所以你最终会为每个状态编写一个函数,并执行"return NEXT_STATE;"之类的操作.甚至看起来像goto.

当然,很难以一种易于理解的方式对状态机进行编码.然而,使用goto没有任何困难,并且通过使用替代控制结构不能减少任何困难.除非您的语言具有"状态机"构造.我没有.

在极少数情况下,您的算法在通过一组有限的允许转换(gotos)连接的节点(状态)的路径中最容易理解,而不是通过任何更具体的控制流(循环,条件,诸如此类) ),然后在代码中应该是明确的.你应该绘制一个漂亮的图表.

setjmp/longjmp可以很好地实现异常或类似异常的行为.虽然没有得到普遍赞扬,但例外通常被认为是"有效"的控制结构.

setjmp/longjmp比goto"更危险",因为它们更难以正确使用,从不介意理解.

从来没有,也没有任何语言可以编写糟糕的代码. - 唐纳德克努特

从C中取出goto不会让在C中编写好的代码变得更容易.实际上,它宁愿错过C 应该能够作为一种美化的汇编语言.

接下来它将是"被认为有害的指针",然后"鸭子打字被认为是有害的".然后,当他们拿走你不安全的编程结构时,谁会留下来为你辩护?嗯?


Personaly,*这*是我给这张支票的评论.我想向读者指出的一件事是,深奥的术语"状态机"包括诸如词法分析器之类的日常事物.查看lex somtime的输出.满满的.
+1状态机上的箭头比任何其他控制结构更紧密地映射到'goto'.当然,你可以在一个循环中使用一个开关 - 就像你可以使用一堆gotos而不是一段时间用于其他问题,但它通常是一个想法; 这是讨论的重点.
您可以在循环(或事件处理程序)中使用switch语句来完成状态机.我已经完成了很多状态机而无需使用jmp或goto.
我可以在最后一段引用你的话吗?
并且,在2013年,我们已经击中(并且有点过去)"指针被认为是有害的"阶段.

7> Marcio Aguia..:

在Linux中:在内核陷阱中使用goto In Kernel Code,与Linus Torvalds和一个关于在Linux代码中使用GOTO的"新人"进行了讨论.那里有一些非常好的点,而Linus穿着那种平常的傲慢:)

一些段落:

Linus:"不,你被CS人员洗脑了,他们认为Niklaus Wirth实际上知道他在说什么.他没有.他没有一个fr c c的线索."

-

Linus:"我认为goto很好,而且它们通常比大量缩进更具可读性."

-

Linus:"当然,在像Pascal这样的愚蠢语言中,标签无法描述,goto可能会很糟糕."


Linus只是(明确地说,就像Rik van Riel在那次讨论中所说的那样)来处理退出状态,并且他是基于C的替代结构在使用它们时会带来的复杂性来实现的.
恕我直言,Linus在这个问题上是正确的.他的观点是,用C语言编写的内核代码需要实现类似于异常处理的东西,最简单和简单地使用goto编写.成语'goto cleanup_and_exit`是goto留下的少数"好"用途之一,因为我们有`for`,`while`和`if`来管理我们的控制流程.另见:http://programmers.stackexchange.com/a/154980
那是一个好点怎么样?他们正在用一种没有别的东西来讨论它的使用.当您在汇编中进行编程时,所有分支和跳转*都是*goto的.而且C是一种"便携式汇编语言".此外,你引用的段落说*没有*关于*为什么*他认为goto是好的.
@mason Classic Mac OS有一些Pascal库(最终--Pascal运行时在早期的Mac中占用了太多内存)但是大多数核心代码都是用Assembler编写的,特别是图形和UI例程.
哇.阅读令人失望.你认为像Linus Torvalds这样的大型操作系统人员会比这更好地了解.Pascal(老派pascal,而不是现代的Object版本)是在68k期间编写的Mac OS Classic,它是当时最先进的操作系统.
是的,Linus是一个傲慢的SOB ......不要介意对整个IT行业做出微不足道的贡献

8> smh..:

在C中,goto仅在当前函数的范围内工作,这往往会定位任何潜在的错误.setjmp并且longjmp更加危险,非本地化,复杂化和依赖于实现.然而,在实践中,它们太模糊,并且不常见导致许多问题.

我相信gotoC 中的危险被夸大了.请记住,最初的goto论点发生在像老式BASIC这样的语言时代,初学者会像这样编写意大利面条代码:

3420 IF A > 2 THEN GOTO 1430

这里Linus描述了一个适当的用法goto:http://www.kernel.org/doc/Documentation/CodingStyle(第7章).


某些版本的BASIC会让你做'GOTO A*40 + B*200 + 30`.不难看出这是多么方便,而且非常危险.
当BASIC首次亮相时,没有任何替代GOTO nnnn和GOSUB mmmm作为跳转的方法.稍后添加结构化构建体.
你错过了这一点......即使那样你也不必写意大利面...你的GOTO总能以一种纪律的方式使用

9> dan04..:

今天,很难看到关于GOTO声明的大问题,因为"结构化编程"人们大多赢得了辩论,今天的语言有足够的控制流结构来避免GOTO.

计算goto现代C程序中的s 数.现在添加的数量break,continuereturn语句.此外,添加的使用次数if,else,while,switchcase.这就是GOTO当你在Dijkstra写下他的信时,如果你在1968年写FORTRAN或BASIC时你的程序会有多少.

当时的编程语言缺乏控制流程.例如,在最初的达特茅斯基础:

IF陈述没有ELSE.如果你想要一个,你必须写:

100 IF NOT condition THEN GOTO 200
...stuff to do if condition is true...
190 GOTO 300
200 REM else
...stuff to do if condition is false...
300 REM end if

即使你的IF陈述不需要ELSE,它仍然只限于一行,通常由一行组成GOTO.

没有DO...LOOP声明.对于非FOR循环,您必须以显式GOTOIF...GOTO回到开头结束循环.

没有SELECT CASE.你必须使用ON...GOTO.

所以,你结束了一个很大GOTO程序中的秒.并且你不能依赖于GOTOs在单个子例程中的限制(因为GOSUB...RETURN子例程的概念很弱),所以这些GOTO可以去任何地方.显然,这使控制流程很难遵循.

这就是反GOTO运动的来源.


+1提及过去。

10> tylerl..:

在某些情况下,Go To可以为"真正的"异常处理提供一种替代.考虑:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

显然,这段代码被简化为占用更少的空间,所以不要太过于挂在细节上.但是考虑一下我在生产代码中看到过多次的替代方法,编码人员为了避免使用goto而荒谬的长度:

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

从功能上讲,这段代码完全相同.实际上,编译器生成的代码几乎完全相同.然而,在程序员热衷于安抚Nogoto(可怕的学术谴责之神)的热情中,这个程序员完全打破了while循环所代表的基本习惯,并对代码的可读性做了一个真实的数字.这不是更好.

所以,故事的寓意是,如果你发现自己为了避免使用goto而采取了一些非常愚蠢的东西,那就不要了.


@DanielAllenLangdon:`break`s在循环中的事实清楚地表明他们*退出循环*.这不是"他们所做的",因为实际上根本没有循环!没有任何东西有可能重复,但直到最后还不清楚.事实上,你有一个"循环"*永远不会*运行多次,这意味着控制结构被滥用.使用`goto`示例,程序员可以说`goto error_handler;`.它更明确,甚至更难以遵循.(Ctrl + F,"error_handler:"查找目标.尝试使用"}"执行此操作.)

11> Bruno Ransch..:

Donald E. Knuth在1992年的CSLI"Literate Programming"一书中回答了这个问题.在p.17有一篇文章" 带有goto语句的结构化编程 "(PDF).我认为这篇文章也可能已在其他书籍中发表过.

这篇文章描述了Dijkstra的建议,并描述了这种情况的有效性.但他也提供了许多反例(问题和算法),这些例子只能使用结构化循环来轻松复制.

本文包含问题的完整描述,历史,示例和反例.



12> Jonathan Lef..:

被Jay Ballou吸引并添加一个答案,我将加上0.02英镑.如果Bruno Ranschaert还没有这样做,我会提到Knuth的"使用GOTO语句进行结构化编程"一文.

我没有看过的一件事就是在Fortran教科书中讲授的一些代码,虽然不常见,但却很常见.像DO循环和开放编码子程序的扩展范围(记住,这将是Fortran II,Fortran IV或Fortran 66 - 而不是Fortran 77或90).语法细节至少有可能是不精确的,但概念应该足够准确.每种情况下的片段都在一个函数内.

请注意,Kernighan&Plauger 撰写的优秀但过时(绝版)的书" The Elements of Programming Style,2nd Edn "包含了一些现实生活中的GOTO滥用例子,这些例子来自其时代(70年代后期)的编程教科书.但是,下面的材料不是那本书.

DO循环的扩展范围

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

这种废话的一个原因是好老式的打卡.您可能会注意到标签(很好地不按顺序,因为这是规范样式!)在第1列(实际上,它们必须在第1-5列中),代码在第7-72列中(第6列是延续标记栏).第73-80列将被赋予序列号,并且有机器将打卡机卡片分类为序列号顺序.如果您的程序在顺序卡上并且需要在循环中间添加一些卡(行),则必须在这些额外行之后重新启动所有内容.但是,如果你用GOTO的东西替换了一张卡片,你可以避免重新测序所有的卡片 - 你只需要在例程结束时用新的序列号将新卡片塞进去.认为这是"绿色计算"的第一次尝试

哦,你可能还会注意到我在欺骗而不是大喊大叫 - Fortran IV通常用大写字母写成.

开放编码的子程序

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

标签76和54之间的GOTO是计算goto的版本.如果变量i的值为1,则转到列表中的第一个标签(123); 如果它的值为2,则转到第二个,依此类推.从76到计算goto的片段是开放编码的子例程.它是一段执行的代码,就像一个子程序,但写在一个函数体中.(Fortran还具有语句功能 - 这些功能是嵌入在单行上的功能.)

有比构成的goto更糟糕的构造 - 你可以为变量分配标签,然后使用指定的goto.谷歌搜索分配goto告诉我它已从Fortran 95中删除.用白色结构编程革命,可以说是公开的Dijkstra的"GOTO Considered Harmful"字母或文章.

如果不了解Fortran所做的各种事情(以及其他语言,其中大多数已被正确地撇开),我们新手很难理解Dijkstra正在处理的问题的范围.哎呀,直到那封信发表十年后我才开始编程(但我确实不幸在Fortran IV中编程了一段时间).


如果你想在野外使用'goto`'看一些代码的例子,问题[通缉:工作Bose-Hibbard排序算法](http://stackoverflow.com/a/4484389/15168)显示了一些( Algol 60)代码发布于1963年.原始布局与现代编码标准不相符.澄清(缩进)代码仍然是相当难以理解的.那里的'goto`语句_do_使得(非常)很难理解算法的用途.

13> thb..:

转到认为有帮助.

我在1975年开始编程.对于20世纪70年代的程序员来说,"转向被认为是有害的"这些词或多或少地表示具有现代控制结构的新编程语言值得尝试.我们确实尝试过新语言.我们很快转换了.我们再也没有回去过.

我们再也没有回去,但是,如果你年轻,那么你从来没有去过那里.

现在,除了作为程序员年龄的指标之外,古代编程语言的背景可能不是很有用.尽管如此,年轻的程序员缺乏这种背景,因此他们不再理解在引入时向其目标受众传达的" 转向被认为有害"的口号.

一个人不理解的标语并不是很有启发性.最好忘记这样的口号.这样的口号没有帮助.

然而,这个特殊的口号"Goto被认为是有害的"已经成为了自己的不死生命.

可以转到不被滥用?答:当然可以,但那又怎样?实际上每个编程元素都可能被滥用.bool例如,谦卑的人比我们想要相信的人更容易受到虐待.

相比之下,我不记得自1990年以来遇到一个单一的,实际的goto滥用实例.

goto最大的问题可能不是技术问题,而是社交问题.有时候不太了解的程序员似乎觉得弃用goto会让他们听起来很聪明.您可能不得不满足这些程序员.这就是人生.

关于goto今天最糟糕的事情是它使用不够.



14> angry person..:

没有GOTO认为有害的东西.

GOTO是一种工具,作为所有工具,它可以被使用和滥用.

然而,编程世界中有许多工具比使用更容易被滥用,而GOTO就是其中之一.Delphi 的WITH语句是另一个.

就个人而言,我不会在典型代码中使用任何一种,但我已经保证了GOTOWITH的奇怪用法,并且替代解决方案将包含更多代码.

最好的解决方案是编译器只是警告你关键字被污染了,你必须在语句周围填写一些pragma指令来摆脱警告.

这就像告诉你的孩子不要用剪刀跑.剪刀也不错,但使用它们可能不是保持健康的最佳方法.



15> smcameron..:

自从我开始在linux内核中做了一些事情以来,getos并没有像以前那样困扰我.起初我有点害怕看到他们(内核人员)在我的代码中添加了getos.我已经习惯于在某些有限的环境中使用gotos,现在偶尔也会使用它们.通常,它是一个转到函数末尾进行某种清理和纾困的goto,而不是在函数中的几个位置复制相同的清理和挽救.通常情况下,它不足以传递给另一个函数 - 例如,释放一些局部(k)malloc变量是典型的情况.

我编写的代码只使用了setjmp/longjmp一次.它是在MIDI鼓音序器程序中.回放发生在与所有用户交互不同的进程中,回放过程使用共享内存和UI进程来获取回放所需的有限信息.当用户想要停止播放时,播放过程只是做了一个"回到开头"的longjmp重新开始,而不是在用户希望它停止时执行的任何地方的一些复杂的展开.它工作得很好,很简单,在这种情况下我从来没有遇到任何与之相关的问题或错误.

setjmp/longjmp有他们的位置 - 但是那个地方是你不可能访问的地方,但很长一段时间.

编辑:我只看了代码.它实际上是我使用的siglongjmp(),而不是longjmp(不是说这是一个大问题,但我忘记了siglongjmp甚至存在.)



16> stesch..:

它永远不会,只要你能够为自己思考.



17> vt...:

如果你用C编写VM,事实证明使用(gcc)计算得到的结果是这样的:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

比循环内的传统开关工作得快得多.


@FredO:这是一个特殊的GCC运营商.但是,除了最严峻的情况之外,我会拒绝这些代码,因为我肯定无法理解wtf的发生.

18> DigitalRoss..:

因为goto可以用于混淆元编程

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

它是低级别的,因为goto是一种原始操作,可以实现更高while或更高的foreach东西.

从某种意义上讲它是高级别的,它以某种方式使用代码以一种清晰的顺序执行,以不间断的方式执行,除了结构化循环,它将它变成逻辑片段,有足够的gotos,抓取 - 动态重组的逻辑包.

所以,有一个平淡邪恶的一面goto.

平淡的侧面是一个向上的goto可以实现一个完全合理的循环和向下指向转到可以做一个完全合理的breakreturn.当然,实际的while,break或者return更具可读性,因为穷人不需要模拟效果,goto以获得全局.所以,一般来说一个坏主意.

邪恶的一面涉及不使用常规的同时,打破或返回跳转,但使用它的什么所谓的意大利面条逻辑.在这种情况下,goto-happy开发人员正在从goto的迷宫中构建代码片段,理解它的唯一方法是在整体上模拟它,当有很多goto时,这是一个非常累人的任务.我的意思是,想象一下评估代码的麻烦,其中的代码else不完全是反转的if,其中嵌套的ifs可能允许某些被外部拒绝的东西if等.

最后,为了真正涵盖这一主题,我们应该注意到,除了Algol之外,基本上所有早期语言最初只根据其版本的单一语句if-then-else.因此,执行条件块的唯一方法是goto使用反条件围绕它.疯了,我知道,但我读过一些陈旧的规格.请记住,第一台计算机是用二进制机器代码编程的,所以我认为任何一种HLL都是救星; 我猜他们对于HLL的具体功能并不太挑剔.

说过我曾经把所有的东西都粘贴goto到我编写的每个程序中"只是为了惹恼纯粹主义者".


+1为烦人的纯粹主义者!:-)

19> 小智..:

拒绝将GOTO声明用于程序员就像告诉木匠不要使用锤子一样,因为它可能会在锤击钉子时损坏墙壁.一个真正的程序员知道如何以及何时使用GOTO.我跟在其中一些所谓的"结构化程序"后面我看到这样的Horrid代码只是为了避免使用GOTO,我可以拍摄程序员.好吧,为了防御另一方,我一次又一次地看到了一些真正的意大利面条代码,那些程序员也应该被拍摄.

这里只是我发现的一个代码示例.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

- - - - - - - - - - - -要么 - - - - - - - - - - -

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10


确实,但更重要的是,真正的程序员知道什么时候*不*使用`goto` - 并且知道*为什么*.避免使用禁忌语言构造,因为$ programming_guru这样说,这就是货物崇拜编程的定义.
DO CRT'这是对的吗?(是/否):':输入YORN = YYN ='Y'或YORN ='N'; 等等

20> 小智..:

"在此链接http://kerneltrap.org/node/553/2131 "

具有讽刺意味的是,消除goto引入了一个错误:省略了spinlock调用.



21> John Milliki..:

原始论文应该被认为是"无条件的GOTO被认为是有害的".它特别提倡一种基于条件(if)和迭代(while)构造的编程形式,而不是早期代码常见的测试和跳转.goto在某些语言或情况下仍然有用,因为没有适当的控制结构.



22> Adam Davis..:

关于我同意Goto 可以使用的唯一地方是当你需要处理错误时,每个特定点发生错误都需要特殊处理.

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

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

总是可以在没有goto的情况下正确地完成它,但在这种情况下和其他几个Goto实际上是更好的解决方案,主要是为了可读性和可维护性.

-亚当



23> Brian Leahy..:

一个现代的GOTO用法是由C#编译器为yield yield定义的枚举数创建状态机.

GOTO应该由编译器而不是程序员使用.


编译器,当然!
您认为谁创造了编译器?
我认为他的意思是"GOTO只能由编译器发出的代码使用".

24> DrPizza..:

直到C和C++(以及其他罪魁祸首)标记了中断并继续,goto将继续发挥作用.


它们不允许在控制流中完全任意跳跃.

25> Mike Dunlave..:

如果GOTO本身是邪恶的,则编译器将是邪恶的,因为它们生成JMP。如果跳入代码块(尤其是在指针之后)本质上是邪恶的,则RETurn指令将是邪恶的。相反,邪恶在于潜在的滥用。

有时我不得不编写必须跟踪多个对象的应用程序,其中每个对象都必须遵循复杂的状态序列才能响应事件,但是整个过程肯定是单线程的。如果用伪代码表示,则典型的状态序列为:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

我确定这不是新事物,但是我在C(++)中处理它的方式是定义一些宏:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

然后(假设状态最初为0),上面的结构化状态机变成结构化代码:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

对此有一个变体,可以有CALL和RETURN,因此某些状态机可以像其他状态机的子例程一样工作。

这不寻常吗?是。维护者需要学习一些知识吗?是。这次学习有回报吗?我认同。可以在没有GOTO跳入块的情况下完成吗?不。

推荐阅读
惬听风吟jyy_802
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有