I've long been under the impression that goto
should never be used if possible. While perusing libavcodec (which is written in C) the other day, I noticed multiple uses of it. Is it ever advantageous to use goto
in a language that supports loops and functions? If so, why?
相关问题
- Multiple sockets for clients to connect to
- What is the best way to do a search in a large fil
- glDrawElements only draws half a quad
- Index of single bit in long integer (in C) [duplic
- Equivalent of std::pair in C
The GOTO can be used, of course, but there is one more important thing than the code style, or if the code is or not readable that you must have in mind when you use it: the code inside may not be as robust as you think.
For instance, look at the following two code snippets:
An equivalent code with GOTO
The first thing we think is that the result of both bits of code will be that "Value of A: 0" (we suppose an execution without parallelism, of course)
That's not correct: in the first sample, A will always be 0, but in the second sample (with the GOTO statement) A might not be 0. Why?
The reason is because from another point of the program I can insert a
GOTO FINAL
without controlling the value of A.This example is very obvious, but as programs get more complicated, the difficulty of seeing those kind of things increases.
Related material can be found into the famous article from Mr. Dijkstra "A case against the GO TO statement"
The most thoughtful and thorough discussion of goto statements, their legitimate uses, and alternative constructs that can be used in place of "virtuous goto statements" but can be abused as easily as goto statements, is Donald Knuth's article "Structured Programming with goto Statements", in the December 1974 Computing Surveys (volume 6, no. 4. pp. 261 - 301).
Not surprisingly, some aspects of this 39-year old paper are dated: Orders-of-magnitude increases in processing power make some of Knuth's performance improvements unnoticeable for moderately sized problems, and new programming-language constructs have been invented since then. (For example, try-catch blocks subsume Zahn's Construct, although they are rarely used in that way.) But Knuth covers all sides of the argument, and should be required reading before anyone rehashes the issue yet again.
Everybody who is anti-
goto
cites, directly or indirectly, Edsger Dijkstra's GoTo Considered Harmful article to substantiate their position. Too bad Dijkstra's article has virtually nothing to do with the waygoto
statements are used these days and thus what the article says has little to no applicability to the modern programming scene. Thegoto
-less meme verges now on a religion, right down to its scriptures dictated from on high, its high priests and the shunning (or worse) of perceived heretics.Let's put Dijkstra's paper into context to shed a little light on the subject.
When Dijkstra wrote his paper the popular languages of the time were unstructured procedural ones like BASIC, FORTRAN (the earlier dialects) and various assembly languages. It was quite common for people using the higher-level languages to jump all over their code base in twisted, contorted threads of execution that gave rise to the term "spaghetti code". You can see this by hopping on over to the classic Trek game written by Mike Mayfield and trying to figure out how things work. Take a few moments to look that over.
THIS is "the unbridled use of the go to statement" that Dijkstra was railing against in his paper in 1968. THIS is the environment he lived in that led him to write that paper. The ability to jump anywhere you like in your code at any point you liked was what he was criticising and demanding be stopped. Comparing that to the anaemic powers of
goto
in C or other such more modern languages is simply risible.I can already hear the raised chants of the cultists as they face the heretic. "But," they will chant, "you can make code very difficult to read with
goto
in C." Oh yeah? You can make code very difficult to read withoutgoto
as well. Like this one:Not a
goto
in sight, so it must be easy to read, right? Or how about this one:No
goto
there either. It must therefore be readable.What's my point with these examples? It's not language features that make unreadable, unmaintainable code. It's not syntax that does it. It's bad programmers that cause this. And bad programmers, as you can see in that above item, can make any language feature unreadable and unusable. Like the
for
loops up there. (You can see them, right?)Now to be fair, some language constructs are easier to abuse than others. If you're a C programmer, however, I'd peer far more closely at about 50% of the uses of
#define
long before I'd go on a crusade againstgoto
!So, for those who've bothered to read this far, there are several key points to note.
goto
statements was written for a programming environment wheregoto
was a lot more potentially damaging than it is in most modern languages that aren't an assembler.goto
because of this is about as rational as saying "I tried to have fun once but didn't like it so now I'm against it".goto
statements in code that cannot be adequately replaced by other constructs.godo
" abomination where an always-falsedo
loop is broken out of usingbreak
in place of agoto
. These are often worse than judicious use ofgoto
.Keep in mind while you're voting me down with one -1 after another that I have used
goto
in my own (non-assembler) code precisely 3 times in the past 15-20 years.I await the flood of outraged shrieks and -1 votes with bated breath.
I've written more than a few lines of assembly language over the years. Ultimately, every high level language compiles down to gotos. Okay, call them "branches" or "jumps" or whatever else, but they're gotos. Can anyone write goto-less assembler?
Now sure, you can point out to a Fortran, C or BASIC programmer that to run riot with gotos is a recipe for spaghetti bolognaise. The answer however is not to avoid them, but to use them carefully.
A knife can be used to prepare food, free someone, or kill someone. Do we do without knives through fear of the latter? Similarly the goto: used carelessly it hinders, used carefully it helps.
C has no multi-level/labelled break, and not all control flows can be easily modelled with C's iteration and decision primitives. gotos go a long way towards redressing these flaws.
Sometimes it's clearer to use a flag variable of some kind to effect a kind of pseudo-multi-level break, but it's not always superior to the goto (at least a goto allows one to easily determine where control goes to, unlike a flag variable), and sometimes you simply don't want to pay the performance price of flags/other contortions to avoid the goto.
libavcodec is a performance-sensitive piece of code. Direct expression of the control flow is probably a priority, because it'll tend to run better.
In Perl, use of a label to "goto" from a loop - using a "last" statement, which is similar to break.
This allows better control over nested loops.
The traditional goto label is supported too, but I'm not sure there are too many instances where this is the only way to achieve what you want - subroutines and loops should suffice for most cases.