GOTO仍然被认为有害吗?
每个人都知道Dijkstra写给编辑的信:转到被认为是有害的声明(也在此.html转录本和.pdf),并且自那时以来一直在大力推动尽可能避免使用goto声明。尽管可以使用goto生成无法维护的庞大代码,但它仍保留在现代编程语言中。甚至Scheme中的高级连续控制结构也可以描述为复杂的goto。
什么情况下需要使用goto?什么时候最好避免?
作为后续问题:C提供了一对函数setjmp和longjmp,它们不仅可以在当前堆栈帧内而且可以在任何调用帧内转到。这些应该像goto一样被视为危险吗?更危险吗?
迪克斯特拉本人对那个头衔感到遗憾,对此他不承担任何责任。在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.
与该主题相匹配的,经过深思熟虑的经典论文是唐纳德·E·努斯(Donald E. Knuth)撰写的《转到语句的结构化编程》。阅读既有助于重新建立上下文,也有助于对主题的非教条式理解。在本文中,Dijkstra对于此案的观点得到了报道,并且更加有力:
Donald E. Knuth: I believe that by presenting such a view I am not in fact disagreeing sharply with Dijkstra's ideas, since he recently wrote the following: "Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"
解决方案
回答
原始论文应被认为是"无条件的GOTO被认为有害"。它特别提倡一种基于条件(" if")和迭代(" while")结构的编程形式,而不是早期代码常见的测试和跳转。在某些没有适当控制结构的语言或者环境中," goto"仍然有用。
回答
我们可以使用它来打破深度嵌套的循环,但是大多数情况下,无需深度嵌套的循环,就可以将代码重构为更简洁的代码。
回答
不会有GOTO认为有害的事情。
GOTO是一种工具,并且作为所有工具,都可以使用和滥用。
但是,编程世界中有许多工具倾向于被滥用而不是被滥用,而GOTO就是其中之一。 Delphi的WITH语句是另一个。
就我个人而言,我都不在典型的代码中使用任何代码,但是我对GOTO和WITH的使用都感到奇怪,这是必须的,另外一种解决方案将包含更多代码。
最好的解决方案是让编译器仅警告我们该关键字已污染,并且我们必须在该语句周围填充一些编译指示,以消除警告。
这就像告诉孩子不要用剪刀奔跑。剪刀还不错,但是使用它们也许不是保持健康的最佳方法。
回答
有时在单个函数中使用GOTO替代异常处理是有效的:
if (f() == false) goto err_cleanup; if (g() == false) goto err_cleanup; if (h() == false) goto err_cleanup; return; err_cleanup: ...
COM代码似乎经常会陷入这种模式。
回答
Ccompiler的一种现代GOTO用法是为由收益率定义的枚举数创建状态机。
GOTO应该由编译器而非程序员使用。
回答
GOTO就像台锯,在采取适当的安全措施时非常有用。
我认为这是有害的,因为大多数初学者都无法同时使用台锯和GOTO。
在某些情况下,它是控制流量的唯一方法,但是可以避免这些情况。
回答
基本思想是goto赋予我们太多自由去做我们不打算做的事情。它可能会在与goto语句无关的地方引起错误,因此使代码维护更加困难。如果我们认为需要goto语句,那就错了:),而应该重新考虑代码的构造。这就是为什么现代编程语言会花很多精力为我们提供可读,可维护的流控制结构以及异常处理机制。
我也不同意lassevk。由于goto被滥用的程度超过了正确使用的程度,因此我认为它在设计良好的语言中没有地位。即使对于goto的"理想"用法,也应该首选需要更多代码的其他实现方式。
因此,总而言之,它仍然被认为是有害的。
回答
关于我唯一可以使用Goto的地方,是我们需要处理错误时,并且发生错误的每个特定点都需要特殊处理。
例如,如果要获取资源并使用信号量或者互斥量,则必须按顺序获取它们,并且应始终以相反的方式释放它们。
一些代码要求获取这些资源的方式很奇怪,我们不能仅仅编写易于维护和理解的控制结构来正确处理这些资源的获取和释放,以避免死锁。
始终可以在没有goto的情况下正确执行此操作,但是在这种情况下,以及其他一些情况,Goto实际上是更好的解决方案,主要用于可读性和可维护性。
-亚当
回答
我的一位同事说,使用GOTO的唯一原因是,如果我们将自己编程到一个角落,这是唯一的出路。换句话说,提前进行适当的设计,以后就不需要使用GOTO了。
我以为这幅漫画很好地说明了"我可以重组程序的流程,或者改用一个小" GOTO"。"当设计较弱时,GOTO是较弱的出路。迅猛龙捕食弱者。
回答
我避免使用它,因为同事/经理无疑会在代码审查中或者当他们偶然发现它时质疑它的使用。虽然我认为它有用途(例如,错误处理案例),但我们会与其他开发人员发生冲突,而他们将对此产生某种类型的问题。
这不值得。
回答
我只需要Basic(即VB,VBScript等)和批处理文件中的它。然后,我仅将其用于错误处理。在Basic中,我倾向于仅使用" on error goto"。在批处理文件中,我必须使用它,因为没有else命令。然后,我仅将它们用作向前跳转到有意义的标签的方法。
回答
在Linux中:在内核陷阱上使用goto在内核代码中,与Linus Torvalds和一个"新手"讨论了在Linux代码中使用GOTO的问题。那里有一些很好的观点,莱纳斯穿着那种通常的傲慢态度:)
一些段落:
Linus: "No, you've been brainwashed by CS people who thought that Niklaus Wirth actually knew what he was talking about. He didn't. He doesn't have a frigging clue."
--
Linus: "I think goto's are fine, and they are often more readable than large amounts of indentation."
--
Linus: "Of course, in stupid languages like Pascal, where labels cannot be descriptive, goto's can be bad."
回答
Donald E. Knuth在1992 CSLI的" Literate Programming"一书中回答了这个问题。在第17有一篇文章"使用goto语句进行结构化编程"(PDF)。我认为该文章可能也已在其他书籍中发表。
本文介绍了Dijkstra的建议,并说明了这种情况的有效情况。但是他还提供了许多反例(问题和算法),这些反例仅使用结构化循环就不容易重现。
本文包含问题的完整说明,历史记录,示例和反例。
回答
我只能使用一次goto来回想一下。我有一系列五个嵌套的计数循环,并且我需要能够根据某些条件从内部从整个结构中突围:
for{ for{ for{ for{ for{ if(stuff){ GOTO ENDOFLOOPS; } } } } } } ENDOFLOOPS:
我可以很容易地声明一个布尔中断变量,并将其用作每个循环的条件条件的一部分,但是在这种情况下,我认为GOTO既实用又可读。
没有迅猛龙袭击我。
回答
我们已经进行了讨论,我支持我的观点。
此外,我厌倦了人们伪装地将高级语言结构描述为" goto",因为他们显然一点都不了解。例如:
Even the advanced continuation control structure in Scheme can be described as a sophisticated goto.
那是完全废话。每个控制结构都可以按照" goto"来实现,但是这种观察是非常琐碎且无用的。 goto被认为不是有害的,因为它具有积极的作用,但是由于它的消极影响,这些已经被结构化编程所消除。
同样,说GOTO是一种工具,并且作为所有工具,可以使用和滥用它完全是不可能的。没有现代的建筑工人会使用岩石来宣称它是一种工具。岩石已被锤子取代。 " goto"已被控制结构取代。如果建筑工人在没有锤子的情况下被困在野外,他当然会改用岩石。如果程序员必须使用不具有功能X的劣等编程语言,那么当然,她可能必须改用goto
。但是,如果她在其他任何地方使用它而不是适当的语言功能,则她显然无法正确理解该语言,并且使用错误。就这么简单。
回答
直到C和C ++(包括其他罪魁祸首)标记为中断并继续运行,goto才会继续发挥作用。
回答
在C语言中," goto"仅在当前函数的范围内起作用,这倾向于将任何潜在的错误本地化。 setjmp和longjmp更加危险,因为它们是非本地的,复杂的并且依赖于实现。但是,在实践中,它们太晦涩难懂且不常见,不会引起很多问题。
我相信C语言中" goto"的危险被大大夸大了。请记住,原始的goto
参数是在老式BASIC之类的语言中出现的,在该语言中,初学者会编写如下的意大利面条式代码:
3420 IF A > 2 THEN GOTO 1430
Linus在这里描述了`goto'的适当用法:http://www.kernel.org/doc/Documentation/CodingStyle(第7章)。
回答
如果我们使用C编写VM,事实证明,使用(gcc)计算的goto像这样:
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; }
其工作比循环内的常规开关快得多。
回答
以下陈述是概括;尽管总是可以提出例外请求,但通常(以我的经验和拙见)不值得冒险。
- 不受限制地使用内存地址(GOTO或者原始指针)会提供太多机会来避免容易避免的错误。
- 到达代码中特定"位置"的方法越多,对该点系统状态的信心就越差。 (见下文。)
- 结构化编程恕我直言不太涉及"避免GOTO",而更多地涉及使代码的结构与数据的结构匹配。例如,重复的数据结构(例如,数组,顺序文件等)由重复的代码单元自然地处理。具有内置的结构(例如while,for,直到,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编译为离线无参数例程来实现,然后使用单个两个参数的VM操作码,该操作码使用条件的布尔值来调用一个或者另一个。这个概念只是"选择现在要调用的内容",而不是"去这里或者去那里"。同样,只是一个隐喻的改变。
回答
使用goto使得编写不太容易维护的"意大利面条代码"变得非常容易。遵循的最重要规则是编写可读代码,但是当然这取决于项目的目标。作为"最佳实践",避免使用goto是个好主意。某些极端的编程类型将其称为"代码异味",因为它表明我们可能做错了什么。循环时使用break与goto非常相似,不同的是它不是goto,但这再次表明代码可能不是最佳的。我认为,这就是为什么没有找到更现代的编程漏洞的原因也很重要,这些漏洞本质上是不同名称的goto。
回答
仅出于此目的,Goto在我要包含在程序中的内容列表中的位置非常低。这并不意味着它是不可接受的。
Goto对于状态机可能很好。循环中的switch语句(按典型重要性顺序)是:(a)实际上并不代表控制流,(b)难看,(c)取决于语言和编译器的潜在效率低下。因此,我们最终会为每个状态编写一个函数,并执行诸如" return NEXT_STATE;"之类的操作。甚至看起来像goto。
当然,很难以易于理解的方式对状态机进行编码。但是,该困难与使用goto无关,并且可以通过使用其他控制结构来减少它。除非语言具有"状态机"构造。我的不是。
在极少数情况下,算法实际上是通过一系列节点(状态)的路径最可理解的,该路径由一组有限的允许转换(元)连接,而不是由任何更特定的控制流(循环,条件,诸如此类)连接),那么该代码应在代码中明确显示。我们应该画一个漂亮的图。
setjmp / longjmp可以很好地实现异常或者类似异常的行为。尽管没有得到普遍的称赞,但异常通常被认为是"有效"的控制结构。
setjmp / longjmp比goto"更危险",因为它们很难正确使用,不要介意。
There never has been, nor will there ever be, any language in which it is the least bit difficult to write bad code. -- Donald Knuth.
将goto移出C语言并不会使在C语言中编写好的代码变得更加容易。实际上,它宁愿错过C语言应该能够充当美化汇编语言的观点。
接下来将是"指针被认为有害",然后是"鸭子输入被认为有害"。那么当他们夺走我们不安全的编程结构时,谁将为我们辩护? ??
回答
在我所看到的每个平台上,高级控制结构都实现为低级getos(跳转)。例如,Java虚拟机具有跳转字节码,但是对于if,else,while,for等则没有。
其中一些编译器为简单的条件块创建意大利面条代码。
要回答问题,仍然认为goto有害的人仍然认为goto有害。 Goto使我们很容易失去结构化编程的优势。
最后,这是程序;因此决定。我建议我们在自己能够回答问题之前,而不是在特定问题的范围内,不要使用goto。
回答
如果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跳入块的情况下完成吗?没有。
回答
有一次,在我编程生涯的早期,我编写了一个程序,该程序由一系列功能组成,这些功能由一个链条组成,其中每个功能在获得成功条件和完成条件后称为其后继程序。
这是一个可怕的问题,有多个严重的问题,最严重的是,直到其下的所有功能都终止,任何功能都无法终止。
但是它开发迅速,可以很好地解决设计中要解决的有限问题,并且可以清楚地显示出程序的逻辑和流程,当我对其进行重构并将其扩展到另一个项目中时,效果很好。
我的投票是在合理的时候使用它,并在方便时尽快将其重构。
回答
向程序员拒绝使用GOTO语句就像告诉木匠不要使用锤子,因为锤子在钉子时会损坏墙壁。真正的程序员知道如何以及何时使用GOTO。 Ive在其中一些所谓的"结构化程序"的后面跟踪,为了避免使用GOTO,我看到了此类Horrid代码,以至于我可以射击程序员。好的,为了反过来,我一次又一次看到了一些真正的意大利面条代码,那些程序员也应该被枪杀。
这只是Ive找到的一个小示例代码。
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,因为从字面上我想不出一种更好(更快)的方式来编写此代码:
我有一个复杂的对象,需要对其进行一些操作。如果对象处于一种状态,则可以执行该操作的快速版本,否则必须执行该操作的较慢版本。问题是,在某些情况下,在慢速操作的中间,有可能意识到这可以通过快速操作来完成。
SomeObject someObject; if (someObject.IsComplex()) // this test is trivial { // begin slow calculations here if (result of calculations) { // just discovered that I could use the fast calculation ! goto Fast_Calculations; } // do the rest of the slow calculations here return; } if (someObject.IsmediumComplex()) // this test is slightly less trivial { Fast_Calculations: // Do fast calculations return; } // object is simple, no calculations needed.
这是实时UI代码中对速度至关重要的一段代码,因此老实说,我认为GOTO在这里是合理的。
雨果
回答
"在此链接http://kerneltrap.org/node/553/2131"
具有讽刺意味的是,消除goto引入了一个错误:自旋锁调用被省略了。
回答
被Jay Ballou添加一个答案所吸引,我将添加0.02. 如果Bruno Ranschaert尚未这样做,我会提到Knuth的"使用GOTO语句进行结构化编程"文章。
我未曾讨论过的一件事是在Fortran教科书中教的那种代码,虽然不是很普遍。诸如DO循环的扩展范围和开放式编码子例程之类的东西(请记住,这应该是Fortran II或者Fortran IV或者Fortran 66而不是Fortran 77或者90)。至少有可能语法细节不正确,但是概念应该足够准确。每种情况下的代码片段都在单个函数内。
请注意,Kernighan&Plauger撰写的优秀但过时(且已绝版)的书《编程风格的元素,第二版》包括一些真实的例子,这些例子滥用了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);如果变量i的值为1,则转到列表中的第一个标签。如果值为2,则转到第二个,依此类推。从76到计算得出的goto的片段是开放编码的子例程。它是一段代码,就像子例程一样执行,但是写在函数主体中。 (Fortran还具有语句函数,这些语句函数是嵌入在一行中的嵌入式函数。)
有比构造的goto更糟糕的构造,我们可以将标签分配给变量,然后使用分配的goto。 Googling指定的goto告诉我,它已从Fortran 95中删除。对结构化编程革命进行了深入讨论,可以说,这是与Dijkstra的" GOTO被认为有害"字母或者文章一起公开开始的。
如果不了解在Fortran(以及其他语言,其中大多数已经正确地落在其他语言中)中所做的各种事情,我们新手很难理解Dijkstra正在处理的问题的范围。哎呀,直到那封信出版十年后,我才开始编程(但是我确实不幸有一阵子无法在Fortran IV中编程)。
回答
尽管我认为最好避免在几乎所有情况下都使用goto,但也有例外。
例如,与其他更为复杂的方法相比,我所见过的一个地方goto语句是一种优雅的解决方案,它是为解释器实现了尾部调用消除功能。
回答
看起来,这是GoTo的好用法,但是在使用垃圾回收器的语言中,我认为使用GoTo的唯一原因是对代码进行混淆(混淆器工具使用GoTo隐藏其代码)
回答
只要我们能够自己思考,它就永远不会。
回答
在某些情况下,转到可以为"实际"异常处理提供某种替代。考虑:
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而求助于某些愚蠢的事情,那就不要这样做。
回答
通常,比起很大的switch语句,要分发的计算式gotos更容易理解。
对于错误和同线程,我认为setcontex或者setjmp(如果可用)更好。
回答
是的,GOTO仍然被认为是有害的。当我们发现自己可能很少使用GOTO时,我们应该对自己的编程技巧有足够的信心,不需要其他人的验证。任何类似于GOTO的函数都会使我们跳到比GOTO所允许的范围更远的范围,应该被认为比GOTO更危险。
回答
自从我开始在linux内核中做一些事情以来,gotos并没有像以前那样困扰我。起初,我很震惊地看到他们(内核人员)在我的代码中添加了goto。从那以后,我已经习惯了在某些有限的上下文中使用gotos,现在偶尔会自己使用它们。通常,它是跳转到函数末尾的某种goto,以进行某种清理和纾困,而不是在函数中的多个位置复制相同的清理和纾困。通常情况下,它还不够大,无法切换到另一个功能-例如释放一些本地(k)malloc分配的变量是一种典型情况。
我写过只使用setjmp / longjmp的代码。它在MIDI鼓音序器程序中。播放是在与所有用户交互操作不同的过程中发生的,并且播放过程使用共享内存与UI流程来获取进行播放所需的有限信息。当用户想要停止播放时,播放过程只是执行了一个"从头开始"的longjmp重新开始,而不是在用户希望停止播放时执行它的任何位置进行一些复杂的展开。它工作得很好,很简单,在那种情况下我从未遇到任何与它相关的问题或者错误。
setjmp / longjmp有他们的地方-但是这个地方是我们不太可能会访问的地方,但很长一段时间后就会出现。
编辑:我只是看了代码。我实际上使用的是siglongjmp(),而不是longjmp(这没什么大不了的,但我忘记了siglongjmp甚至存在。)
回答
当我们生成C状态机时,使用GOTO可能会很好。我永远不会在手写代码"现代"语言结构中使用GOTO,因此完全没有必要。
setjmp / longjmp构造在某些情况下可能有用(当缺少" true"异常时,或者当我们实现类似Chicken方案时),但在"常规"编程中没有位置。
回答
在理想的世界中,我们永远不需要GOTO。但是,我们生活在一个不完美的世界中。我们没有具有我们梦every以求的每个控制结构的编译器。有时候,我觉得使用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的使用施加限制,从而降低了潜在风险。例如,C不允许我们使用GOTO从循环外部跳入循环体。文档中提到了限制。
这是GOTO有时比以前更安全的一个例子。
在某些情况下,GOTO的使用与从函数中早返回(即早退出循环)相同。但是可以争论好的形式。
回答
因为goto
可用于混淆元编程
" Goto"既是高级控制表达式,也是低级控制表达式,因此,它没有适合大多数问题的适当设计模式。
从某种意义上说,goto是一种底层操作,它实现了诸如while
或者foreach
之类的高级操作。
从某种意义上说,它是高级的,当它以某种方式使用时,它将以清晰的顺序,以不间断的方式执行代码(结构化循环除外),并将其更改为具有足够的" goto"数量的逻辑部分。 ,可以动态重组逻辑的抓包。
因此," goto"存在平淡无奇的一面。
平庸的一面是,向上指向的goto可以实现一个完全合理的循环,而向下指向的goto可以执行一个完全合理的" break"或者" return"。当然,实际的" while"," break"或者" return"将更具可读性,因为穷人不必为了获得全局而模拟" goto"的效果。因此,这是一个坏主意。
邪恶的一面涉及一个例程,该例程不使用goto进行while,break或者return而是将其用于所谓的意大利面条逻辑。在这种情况下,对goto满意的开发人员将在goto的迷宫中构建代码段,而唯一的理解方法是从整体上进行模拟,这在很多goto的情况下是一件非常累人的任务。我的意思是,想象一下评估代码的麻烦,其中" else"并非恰好是" if"的逆,而嵌套的" if"在某些情况下可能会被外部" if"拒绝,等等,等等。
最后,要真正涵盖该主题,我们应该注意,除Algol之外,基本上所有早期语言最初仅根据其if-then-else版本进行单个声明。因此,执行条件块的唯一方法是使用逆条件块围绕它进行"转到"操作。我知道这很疯狂,但是我已经阅读了一些旧规范。请记住,第一批计算机是用二进制机器代码编程的,所以我认为任何一种HLL都是救命稻草。我想他们对所获得的HLL功能究竟有多挑剔。
说了这么多,我曾经在每一个程序中都贴上" goto"字样,"只是为了惹恼纯粹主义者"。
回答
今天,很难看到关于" GOTO"语句的大问题,因为"结构化编程"人员大都赢得了辩论,并且当今的语言具有足够的控制流结构来避免使用" GOTO"。
在现代C程序中计算goto
的数量。现在添加" break"," continue"和" return"语句的数量。此外,添加使用" if"," else"," while"," switch"或者" case"的次数。那就是如果我们在1968年Dijkstra写信时使用FORTRAN或者BASIC编写程序时,程序将有多少个" GOTO"程序。
当时的编程语言缺乏控制流程。例如,在原始的Dartmouth 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
循环,我们必须以明确的GOTO
或者IF ... GOTO
结束循环。 - 没有
SELECT CASE
。我们必须使用ON ... GOTO
。
因此,我们在程序中得到了很多GOTO
。而且我们不能依赖于GOTO
在单个子程序中的限制(因为" GOSUB ... RETURN"是子程序的一个弱概念),所以这些GOTO
s可以在任何地方使用。显然,这使得控制流程难以遵循。
这就是反GOTO运动的起源。
回答
在我看来,"变得有害"更多地是关于状态的封装和一致性。
许多代码,甚至是" oo"代码,都具有像任何意大利面条代码一样糟糕的混乱状态封装。
" goto认为是有害的"的问题在于,它使程序员只看机械规则而没有理解印象,即唯一可用的流程控制应该是return方法,并且很容易导致传递很多状态通过引用,这直接导致缺乏状态封装,"被认为有害"的事情正试图摆脱。
遵循典型的" OO"代码库中的控制流,并告诉我们我们还没有意大利面条代码...。(顺便说一句,我并不是说通常会非常讨厌路径的" ravioli"代码即使对象关系不是立即很明显的,馄饨代码的执行通常也非常简单。
或者,换句话说,仅当每个子例程仅修改局部状态(除非通过该子例程(或者至少是该对象)才能修改)时,才避免使用goto来支持一切都是子例程。
回答
在几乎所有可以使用goto的情况下,我们都可以使用其他构造来执行相同的操作。无论如何,编译器都会使用Goto。
我个人从来没有明确使用它,也没有必要使用它。
回答
goto存在一些问题。一是很难看清代码的流向。由于花括号的原因,更容易查看if块,但是goto会将其隐藏起来。另外,虽然和如果也从本质上讲也是,但是它们有助于解释为什么在代码中来回跳动。使用常规的goto,我们必须自己拼凑起来。
作为练习,尝试编写一些代码来计算斐波那契数列,并查看完成后的阅读难度。
如果我们要使用该代码,那么我建议编写一些单元测试并重写它。否则,随它去吧。
总而言之,有时出于性能原因,使用goto可能是适当的。
回答
并不是说goto本身就是坏的。当可以用另一种方式更清楚地表达相同的逻辑时,使用goto是不好的。它会使代码很难遵循并且难以维护。只需从Bad Old Days看一些Basic中的程序作为示例。
我认为,在正常情况下,像Cwe这样的现代语言永远都不需要goto。如果我发现自己正在使用它,通常这是我需要重新思考逻辑的标志-几乎可以肯定,有一种使用普通代码流语句表达相同代码的更清晰方法。
也就是说,对于goto来说,有一些特殊用途非常有用(我发现自己对没有它的语言感到恼火)。我主要在C语言中使用它来突破多个级别的循环或者进行错误处理。我相信Chas语言功能意味着我们不必这样做。 (在生成自动生成的代码时,它也非常有用,但这并不是大多数人在现实生活中遇到的。)
goto还有另一个问题,它纯粹是政治性的:很多人都讨厌它,即使有理由在代码中使用它也会引起问题。如果这是赋值代码,则请重写它,否则我们可能会被打分。否则,我倾向于将其保留,直到下次我们需要对该部分进行维护时为止。
回答
我认为在Excel中使用Velociraptor是安全的。