GOTO仍然认为有害?
每个人都知道Dijkstra 给编辑的信件:去看看被认为是有害的声明 (也在这里 .html成绩单和here.pdf),从那时起,已经有一个强大的推动,尽可能避开goto声明。 尽pipe可以使用goto来生成不可维护,庞大的代码,但仍然保留在现代编程语言中 。 即使是Scheme中先进的继续控制结构也可以被描述成一个复杂的goto。
什么情况下可以使用goto? 什么时候最好避免?
作为一个后续问题:C提供了一对函数setjmp和longjmp,它们提供了不仅在当前栈帧内而且在任何调用帧内跳转的function。 这些被认为是危险的吗? 更危险?
迪克斯特拉本人对这个称号感到后悔,对此他不负责任。 在EWD1308 (也在这里 .pdf)的末尾,他写道:
最后一个小故事的logging。 1968年,ACM通讯刊登了一篇题为“ goto声明被认为是有害的 ”的文本,而在后来的这些文章中,这个文章经常被引用,然而令人遗憾的是,经常被那些看不到它的作者标题,通过成为一个模板,成为了我的名气的基石:几乎所有的X,包括一个名为“Dijkstra认为有害”的标题,我们会看到各种各样的标题“X认为有害的”。 但是发生了什么? 我已经提交了一个名为“ 一个反对goto声明的案子 ”的文章,为了加快出版速度,编辑变成了“给编辑的一封信”,在这个过程中,他给了它一个新的他自己发明的头衔! 编辑是Niklaus Wirth。
关于这个主题的经过深思熟虑的经典论文,与Dijkstra相匹配的是由Donald E. Knuth撰写的“ 结构化程序devise” 。 阅读都有助于重新build立背景和对这个问题的非教条理解。 在这篇论文中,迪克斯特拉对这个案子的看法是有报道的,而且更为强烈:
Donald E. Knuth:我相信通过提出这样的观点,我并不认同迪克斯特拉的观点,因为他最近写了这样一段话:“请不要陷入相信我对于去声明] 我有这样一种不舒服的感觉,就是其他人正在把它当作宗教,就好像编程的概念问题可以通过一个简单的编程技巧来解决!
以下陈述是概括性的; 虽然总是有可能申请例外,但通常(以我的经验和谦虚的意见)是不值得冒险的。
- 内存地址的无约束使用(GOTO或原始指针)提供了太多的机会来制作容易避免的错误。
- 在代码中到达某个特定“位置”的方法越多,那么系统状态到底是什么就越不自信。 (见下文。)
- 结构化编程恕我直言,“避免GOTO”更多的是关于使代码的结构与数据的结构相匹配。 例如,重复的数据结构(例如数组,序列文件等)自然是由重复的代码单元处理的。 内置结构(例如while,for,until,each等)允许程序员避免重复相同的陈旧代码模式的繁琐。
- 即使GOTO是低级实现细节(并不总是如此),它低于程序员应该考虑的级别。 有多less程序员在原始二进制文件中平衡个人支票簿? 有多less程序员担心磁盘上的哪个扇区包含特定的logging,而不是仅仅为数据库引擎提供密钥(如果我们真的用物理磁盘扇区编写了所有的程序,会出现多less错误?
上述脚注:
关于第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
之间的关系充满信心。 (事实上,在一般情况下,这是不可取的!)
关于第四点,代码中“去某处”的概念只是一个比喻。 除了电子和光子(废热)之外,没有任何东西在CPU内部的任何地方“走”。 有时我们放弃另一个更有用的比喻。 我记得遇到(几十年前!)一个语言在哪里
if (some condition) { action-1 } else { action-2 }
通过将action-1和action-2编译为非线性无参数例程,然后使用单个双参数VM操作码(使用条件的布尔值来调用其中一个)来在虚拟机上实现。 这个概念只是“select现在调用的内容”,而不是“去这里或去那里”。 再次,只是一个比喻的变化。
我的一个同事说使用GOTO的唯一理由是如果你把自己编程到一个angular落,那么这是唯一的出路。 换句话说,提前进行适当的devise,以后不需要使用GOTO。
我认为这个漫画很好地说明了这一点:“我可以调整程序的stream程,或者用一个小小的'GOTO'代替。 当你有弱devise时,GOTO是一个弱点。 肉食动物掠夺弱者 。
有时在单个函数中使用GOTO作为exception处理的替代方法是有效的:
if (f() == false) goto err_cleanup; if (g() == false) goto err_cleanup; if (h() == false) goto err_cleanup; return; err_cleanup: ...
COM代码似乎经常陷入这种模式。
我只能记得一次使用goto。 我有一系列的五个嵌套计数循环,我需要能够从内部早期基于某些条件突破整个结构:
for{ for{ for{ for{ for{ if(stuff){ GOTO ENDOFLOOPS; } } } } } } ENDOFLOOPS:
我可以轻松地声明一个布尔中断variables,并将其用作每个循环的条件的一部分,但是在这种情况下,我决定使用GOTO同样可行并且可读。
没有肉食动物袭击我。
我们已经讨论过了 ,我坚持我的观点 。
此外,我厌倦了把高级语言结构描述为“变相”的人,因为他们显然根本就没有这个意思。 例如:
即使是Scheme中先进的继续控制结构也可以被描述成一个复杂的goto。
那完全是无稽之谈。 每个控制结构都可以用goto
来实现,但是这个观察是完全没有用的。 goto
不会被认为是有害的,因为它有积极的影响,但是由于它的负面影响,这些被结构化编程所淘汰。
同样,“GOTO是一种工具,所有工具都可以被使用和滥用”是完全不合格的。 没有一个现代build筑工人会使用岩石,并声称它是“一种工具”。岩石已被锤子取代。 goto
已被控制结构取代。 如果build筑工人在没有锤子的情况下被困在野外,他当然会用石头代替。 如果一个程序员必须使用一个没有特性X的低级编程语言,那么当然她可能不得不使用goto
。 但如果她在其他任何地方使用它,而不是适当的语言特征,她显然没有正确理解语言并错误地使用它。 这真的很简单。
为了这个目的,Goto在我的程序清单上非常低。 这并不意味着这是不可接受的。
转到可以很好的状态机。 循环中的switch语句(按照通常的重要性顺序):(a)实际上不代表控制stream,(b)丑,(c)取决于语言和编译器,可能效率低下。 所以你最终在每个状态下编写一个函数,并执行“return NEXT_STATE;” 甚至看起来像转到。
当然,很难用易于理解的方式对状态机进行编码。 然而,使用goto没有任何困难,使用替代的控制结构也不能减less它。 除非你的语言有一个“状态机”构造。 我的不是。
在极less数情况下,当你的algorithm在通过一系列有限的允许转换(gotos)连接的一系列节点(状态)的path上真正是最易理解的,而不是任何更具体的控制stream(循环,条件,whatnot ),那么在代码中应该是明确的。 你应该绘制一个漂亮的图表。
setjmp / longjmp可以很好地实现exception或类似exception的行为。 虽然没有得到普遍赞扬,但例外情况通常被认为是“有效的”控制结构。
setjmp / longjmp比goto更“危险”,因为它们难以正确使用,不用理解。
从来没有,也不会有任何语言,在这种语言中编写错误的代码是最难的。 Donald Knuth。
从C中跳出来并不会让在C中编写好的代码变得更容易。事实上,它宁可错过C 应该能够成为汇编语言的光荣点。
接下来会是“有害的指针”,然后是“鸭认为有害”。 那么当他们来拿走你的不安全的编程结构时,谁将会留下来为你辩护呢? 嗯?
在Linux中:使用goto在Kernel Trap上的内核代码中 ,与Linus Torvalds以及在Linux代码中使用GOTO的“新人”进行了讨论。 那里有一些非常好的点,莱纳斯穿着平常的傲慢:)
有些段落:
“Linus:”不,你被CS认为是Niklaus Wirth真正知道他在说什么的人洗脑,他没有,他没有一个吓人的线索。
–
Linus:“我认为goto很好,而且通常比大量的缩进更可读。”
–
Linus:“当然,在帕斯卡这样的愚蠢的语言里,标签不能描述,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章)。
今天,很难看到关于GOTO
声明的大事,因为“结构化编程”人员大多赢得辩论,今天的语言有足够的控制stream程结构来避免GOTO
。
在现代C程序中计算转换次数。 现在添加break
, continue
和return
语句的数量。 此外,添加您使用if
, else
, while
, switch
或case
。 这就是说,如果你在1968年Dijkstra写信的时候写了FORTRAN或BASIC,你的程序会有多lessGOTO
。
当时的编程语言缺乏控制stream程。 例如,在原来的达特茅斯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
可以去任何地方 。 显然,这使得控制stream很难遵循。
这是反GOTO
运动的来源。
在某些情况下,Go To可以提供一种“真正的”exception处理的替身。 考虑:
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;
显然这个代码被简化了占用更less的空间,所以不要太在细节上挂了。 但考虑一下我在代码生产中看过太多次的代码,为了避免使用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);
现在function上这个代码完全一样的东西。 事实上,编译器生成的代码几乎是相同的。 然而,程序员热衷于安抚Nogoto (这个令人畏惧的学术责备之神),这个程序员已经完全打破了while
循环所代表的底层习惯,并在代码的可读性上做了一个真实的数字。 这不是更好。
所以,故事的寓意是,如果你发现自己为了避免使用goto而采取了一些非常愚蠢的行为,那么就不要这样做。
Donald E. Knuth在1992年的“文学编程”一书中回答了这个问题。 第6页。 17有一篇文章“ 使用goto语句进行结构化编程 ”(PDF)。 我认为这篇文章也可能在其他书中发表。
这篇文章描述了迪克斯特拉的build议,并描述了这种情况。 但是他也给出了一些反例(问题和algorithm),这些例子不能用结构化的循环来简单地重现。
文章包含了对问题的完整描述,历史,例子和计数器例子。
Jay Ballou吸引了一个答案,我将增加我的0.02英镑。 如果Bruno Ranschaert还没有这样做,我会提到Knuth的“使用GOTO语句的结构化编程”一文。
有一件事我没有见过,那就是Fortran教科书中教授的那种代码。 像DO循环的扩展范围和开放编码的子程序(记住,这将是Fortran II,或Fortran IV,或Fortran 66 – 而不是Fortran 77或90)。 至less有一个机会,句法细节是不准确的,但概念应该足够准确。 每种情况下的片段都在一个函数内。
请注意,由Kernighan&Plauger 编写的“编程风格的元素,第二版 ”的优秀但是过时的(绝版)书籍包含了一些从其时代(70年代后期)编程教科书中滥用GOTO的现实例子。 下面的材料不是来自那本书,但是。
扩展的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列将被给出序列号,并且有机器将打卡卡片分类成序列号顺序。 如果你在顺序卡上有你的程序,并且需要在循环的中间添加几张卡(线),那么你必须在这些额外的行之后重新执行一切。 但是,如果您用GOTOreplace了一张卡,则可以避免重新sorting所有卡 – 只需在新程序末尾将新卡放入新序列号即可。 考虑这是“绿色计算”的第一次尝试 – 节省打卡(或更具体地说,节省重新打字的劳动力 – 并节省相应的更新错误)。
哦,你也可能会注意到我是在作弊而不是喊叫 – 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的一个版本。 如果variablesi的值为1,则转到列表中的第一个标签(123); 如果它有值2,转到第二个,等等。 从76到计算的goto的片段是开放编码子程序。 这是一段执行的代码,就像一个子程序,但写在一个函数的主体中。 (Fortran也有声明函数 – 这些函数是embedded在一行代码中的函数。)
比计算出的goto有更糟糕的结构 – 你可以给variables分配标签,然后使用分配的goto。 Googling assigned goto tells me it was deleted from Fortran 95. Chalk one up for the structured programming revolution which could fairly be said to have started in public with Dijkstra's "GOTO Considered Harmful" letter or article.
Without some knowledge of the sorts of things that were done in Fortran (and in other languages, most of which have rightly fallen by the wayside), it is hard for us newcomers to understand the scope of the problem which Dijkstra was dealing with. Heck, I didn't start programming until ten years after that letter was published (but I did have the misfortune to program in Fortran IV for a while).
Goto considered helpful.
I started programming in 1975. To 1970s-era programmers, the words "goto considered harmful" said more or less that new programming languages with modern control structures were worth trying. We did try the new languages. We quickly converted. We never went back.
We never went back, but, if you are younger, then you have never been there in the first place.
Now, a background in ancient programming languages may not be very useful except as an indicator of the programmer's age. Notwithstanding, younger programmers lack this background, so they no longer understand the message the slogan "goto considered harmful" conveyed to its intended audience at the time it was introduced.
Slogans one does not understand are not very illuminating. It is probably best to forget such slogans. Such slogans do not help.
This particular slogan however, "Goto considered harmful," has taken on an undead life of its own.
Can goto not be abused? Answer: sure, but so what? Practically every programming element can be abused. The humble bool
for example is abused more often than some of us would like to believe.
By contrast, I cannot remember meeting a single, actual instance of goto abuse since 1990.
The biggest problem with goto is probably not technical but social. Programmers who do not know very much sometimes seem to feel that deprecating goto makes them sound smart. You might have to satisfy such programmers from time to time. Such is life.
The worst thing about goto today is that it is not used enough.
There is no such things as GOTO considered harmful .
GOTO is a tool, and as all tools, it can be used and abused .
There are, however, many tools in the programming world that have a tendency to be abused more than being used , and GOTO is one of them. the WITH statement of Delphi is another.
Personally I don't use either in typical code , but I've had the odd usage of both GOTO and WITH that were warranted, and an alternative solution would've contained more code.
The best solution would be for the compiler to just warn you that the keyword was tainted , and you'd have to stuff a couple of pragma directives around the statement to get rid of the warnings.
It's like telling your kids to not run with scissors . Scissors are not bad, but some usage of them are perhaps not the best way to keep your health.
Since I began doing a few things in the linux kernel, gotos don't bother me so much as they once did. At first I was sort of horrified to see they (kernel guys) added gotos into my code. I've since become accustomed to the use of gotos, in some limited contexts, and will now occasionally use them myself. Typically, it's a goto that jumps to the end of a function to do some kind of cleanup and bail out, rather than duplicating that same cleanup and bailout in several places in the function. And typically, it's not something large enough to hand off to another function — eg freeing some locally (k)malloc'ed variables is a typical case.
I've written code that used setjmp/longjmp only once. It was in a MIDI drum sequencer program. Playback happened in a separate process from all user interaction, and the playback process used shared memory with the UI process to get the limited info it needed to do the playback. When the user wanted to stop playback, the playback process just did a longjmp "back to the beginning" to start over, rather than some complicated unwinding of wherever it happened to be executing when the user wanted it to stop. It worked great, was simple, and I never had any problems or bugs related to it in that instance.
setjmp/longjmp have their place — but that place is one you'll not likely visit but once in a very long while.
Edit: I just looked at the code. It was actually siglongjmp() that I used, not longjmp (not that it's a big deal, but I had forgotten that siglongjmp even existed.)
It never was, as long as you were able to think for yourself.
If you're writing a VM in C, it turns out that using (gcc's) computed gotos like this:
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; }
works much faster than the conventional switch inside a loop.
Because goto
can be used for confusing metaprogramming
Goto
is both a high-level and a low-level control expression, and as a result it just doesn't have a appropriate design pattern suitable for most problems.
It's low-level in the sense that a goto is a primitive operation that implements something higher like while
or foreach
or something.
It's high-level in the sense that when used in certain ways it takes code that executes in a clear sequence, in an uninterrupted fashion, except for structured loops, and it changes it into pieces of logic that are, with enough goto
s, a grab-bag of logic being dynamically reassembled.
So, there is a prosaic and an evil side to goto
.
The prosaic side is that an upward pointing goto can implement a perfectly reasonable loop and a downward-pointing goto can do a perfectly reasonable break
or return
. Of course, an actual while
, break
, or return
would be a lot more readable, as the poor human wouldn't have to simulate the effect of the goto
in order to get the big picture. So, a bad idea in general.
The evil side involves a routine not using goto for while, break, or return, but using it for what's called spaghetti logic . In this case the goto-happy developer is constructing pieces of code out of a maze of goto's, and the only way to understand it is to simulate it mentally as a whole, a terribly tiring task when there are many goto's. I mean, imagine the trouble of evaluating code where the else
is not precisely an inverse of the if
, where nested if
s might allow in some things that were rejected by the outer if
, etc, etc.
Finally, to really cover the subject, we should note that essentially all early languages except Algol initially made only single statements subject to their versions of if-then-else
. So, the only way to do a conditional block was to goto
around it using an inverse conditional. Insane, I know, but I've read some old specs. Remember that the first computers were programmed in binary machine code so I suppose any kind of an HLL was a lifesaver; I guess they weren't too picky about exactly what HLL features they got.
Having said all that I used to stick one goto
into every program I wrote "just to annoy the purists" .
Denying the use of the GOTO statement to programmers is like telling a carpenter not to use a hammer as it Might damage the wall while he is hammering in a nail. A real programmer Knows How and When to use a GOTO. I've followed behind some of these so-called 'Structured Programs' I've see such Horrid code just to avoid using a GOTO, that I could shoot the programmer. Ok, In defense of the other side, I've seen some real spaghetti code too and again, those programmers should be shot too.
Here is just one small example of code I've found.
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
———————–OR———————-
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
The original paper should be thought of as "Unconditional GOTO Considered Harmful". It was in particular advocating a form of programming based on conditional ( if
) and iterative ( while
) constructs, rather than the test-and-jump common to early code. goto
is still useful in some languages or circumstances, where no appropriate control structure exists.
About the only place I agree Goto could be used is when you need to deal with errors, and each particular point an error occurs requires special handling.
For instance, if you're grabbing resources and using semaphores or mutexes, you have to grab them in order and you should always release them in the opposite manner.
Some code requires a very odd pattern of grabbing these resources, and you can't just write an easily maintained and understood control structure to correctly handle both the grabbing and releasing of these resources to avoid deadlock.
It's always possible to do it right without goto, but in this case and a few others Goto is actually the better solution primarily for readability and maintainability.
-亚当
"In this link http://kerneltrap.org/node/553/2131 "
Ironically, eliminating the goto introduced a bug: the spinlock call was omitted.
Until C and C++ (amongst other culprits) have labelled breaks and continues, goto will continue to have a role.
One modern GOTO usage is by the C# compiler to create state machines for enumerators defined by yield return.
GOTO is something that should be used by compilers and not programmers.
I avoid it since a coworker/manager will undoubtedly question its use either in a code review or when they stumble across it. While I think it has uses (the error handling case for example) – you'll run afoul of some other developer who will have some type of problem with it.
It's not worth it.
If GOTO itself were evil, compilers would be evil, because they generate JMPs. If jumping into a block of code, especially following a pointer, were inherently evil, the RETurn instruction would be evil. Rather, the evil is in the potential for abuse.
At times I have had to write apps that had to keep track of a number of objects where each object had to follow an intricate sequence of states in response to events, but the whole thing was definitely single-thread. A typical sequence of states, if represented in pseudo-code would be:
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
I'm sure this is not new, but the way I handled it in C(++) was to define some macros:
#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 ...
Then (assuming state is initially 0) the structured state machine above turns into the structured code:
{ 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; }
With a variation on this, there can be CALL and RETURN, so some state machines can act like subroutines of other state machines.
Is it unusual? 是。 Does it take some learning on the part of the maintainer? 是。 Does that learning pay off? 我想是这样。 Could it be done without GOTOs that jump into blocks? 不。
I actually found myself forced to use a goto, because I literally couldn't think of a better (faster) way to write this code:
I had a complex object, and I needed to do some operation on it. If the object was in one state, then I could do a quick version of the operation, otherwise I had to do a slow version of the operation. The thing was that in some cases, in the middle of the slow operation, it was possible to realise that this could have been done with the fast operation.
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.
This was in a speed critical piece of realtime UI code, so I honestly think that a GOTO was justified here.
雨果
Almost all situations where a goto can be used, you can do the same using other constructs. Goto is used by the compiler anyway.
I personally never use it explicitly, don't ever need to.
One thing I've not seen from any of the answers here is that a 'goto' solution is often more efficient than one of the structured programming solutions often mentioned.
Consider the many-nested-loops case, where using 'goto' instead of a bunch of if(breakVariable)
sections is obviously more efficient. The solution "Put your loops in a function and use return" is often totally unreasonable. In the likely case that the loops are using local variables, you now have to pass them all through function parameters, potentially handling loads of extra headaches that arise from that.
Now consider the cleanup case, which I've used myself quite often, and is so common as to have presumably been responsible for the try{} catch {} structure not available in many languages. The number of checks and extra variables that are required to accomplish the same thing are far worse than the one or two instructions to make the jump, and again, the additional function solution is not a solution at all. You can't tell me that's more manageable or more readable.
Now code space, stack usage, and execution time may not matter enough in many situations to many programmers, but when you're in an embedded environment with only 2KB of code space to work with, 50 bytes of extra instructions to avoid one clearly defined 'goto' is just laughable, and this is not as rare a situation as many high-level programmers believe.
The statement that 'goto is harmful' was very helpful in moving towards structured programming, even if it was always an over-generalization. At this point, we've all heard it enough to be wary of using it (as we should). When it's obviously the right tool for the job, we don't need to be scared of it.
You can use it for breaking from a deeply nested loop, but most of the time your code can be refactored to be cleaner without deeply nested loops.