Empirical Study On How C Devs Use Goto In Practice Says "Not Harmful"
Edsger Dijkstra famously opined in 1968 on the danger of Goto statements. New submitter Mei Nagappan writes with a mellower view, nearly 50 years later: By qualitatively and quantitatively analyzing a statistically valid random sample from almost 2 million C files and 11K+ projects, we find that developers limit themselves to using goto appropriately in most cases, and not in an unrestricted manner like Dijkstra feared, thus suggesting that goto does not appear to be harmful in practice.
(Here's the preprint linked from above abstract.)
It gives me so much more flexibility and power. The computed comfrom is even better.
Is that because they were warned by Djikstra that it would be harmful to use it haphazardly? Or is it for some other reason?
Obligatory: http://xkcd.com/292/
Goto is being used safely (relatively) now, but would have the programming practices that cause that to be true become so prevalent without his warning that it had potential for problems?
There is an implication that Dijkstra was wrong about the goto - the implication being based on how conservatively it is used.
Perhaps it is wiser to conclude that the goto is used so conservatively because Dijkstra was right and that programmers have, in general, taken his wisdom to heart and avoided the goto except for those instances where, properly documented, it is the best tool for the job.
(By prophylactic prediction I mean the sort of warning or planning that completely forestalls the danger predicted, through awareness, preparation, etc. Kind of like the Y2K non-event.)
I'm here EdgeKeep Inc.
https://www.imperialviolet.org/2014/02/22/applebug.html
Because we all got the warning, and thus did not write horrible goto-festooned spaghetti code and only use goto when appropriate. This means Dijkstra's letter was a success. Also, it was Niklaus Wirth who decided to use the "considered harmful" verbiage, not Dijkstra.
All languages can be abused.
goto is no different than if nests or giant nested for loops or any other maddening crap people can come up with. If you look at the code most C compilers come up with they are not afraid of a goto...
Goto used correctly is a good tool. Its just a tool. Do not treat it as something bad or good. Look to how it is used.
This makes sense for a couple reasons.
First, abusing goto really serves noone. It doesn't make code quicker to write. It certainly doesn't make it easier to understand. There is no benefit to it.
Second, I'd argue that very few people want to write new code in C these days. Those who do have specific reasons for it and are probably a bit more experienced or passionate and thus aren't the kinds of people who'd readily abuse things. The ones who would are going to be mostly attracted to easier high-level languages that don't allow the abuse in the first place.
I'm not a "hard core" coder (defined here as someone who does nothing but development) but I've been writing C for 15+ years (it was the first language I learned and remains my favorite) and have yet to encounter a situation where the use of 'goto' is a requirement or even better than the alternative. The one circumstance where I've seen it advocated is for the main loop of a long running program but I'm not certain why goto is any better than while (1) (or similar constructs) in this scenario. Correct me if I'm wrong but the main argument against goto is that it results in haphazard code that's hard to follow. I think this is true, even in the simple case of using goto to replace while (1), never mind the more convoluted examples that invariably result when goto enters the equation.
So, people who are smarter than me, what am I overlooking? What C coding scenario presents itself where goto is the most eloquent solution?
I want peace on earth and goodwill toward man.
We are the United States Government! We don't do that sort of thing.
I bet the "valid random sample" didn't include any projects from the Obfuscated C Code Contest.
Knowledge is how to play a game, intelligence is how to win, wisdom is knowing what game to play.
void func() {
if (!AquireResource1()) goto end;
if (!AquireResource2()) goto cleanup1;
if (!AquireResource3()) goto cleanup2:
DoStuffWithResources();
Cleanup3();
cleanup2:
Cleanup2();
cleanup1:
Cleanup1();
end:
return;
}
Headline should read "Thanks to Dijkstra's warning, GOTO in practice not harmful."
Support the First Amendment. Read at -1
It's no surprise that goto's are used sparingly - and generally only with very good reason - by modern programmers. Dijkstra's paper is dated 1968, which is about the time ALGOL was invented. ALGOL which was the first block-oriented language. Heck, maybe its design was even influenced by Dijkstra's paper - who knows?
Given a language that supports blocks (and all modern languages do), there's little reason to use gotos. Instead, you use blocks and related goto-like constructs such as break, return, try/catch, etc. Anything but a literal goto.
IIRC, the CPython source code has a few goto's in it. I remember that Tim Peters once defended that as being the best solution (in C, at least) for certain very exceptional situations. It's no coincidence that they're used very, very sparingly there. And maybe they wouldn't be needed at all in CPython if C had a try/catch construct - like Python itself.
In my own case, I've used them mostly when transliterating some ancient FORTRAN code into C. Rather than untangling the code into blocks, it was simply easier to replicate the goto's. At the time, I actually had to consult K&R to brush up on C's goto syntax. Also, the FORTRAN code I was working with was proven and needed little maintenance, so removing the goto's was more likely to make it worse than to make it better.
void func() {
if (AquireResource1()){
if (!AquireResource2()){
if (!AquireResource3()){
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
return;
}
Fundamentally, that *is* a GOTO. It's just a less-readable idiomatic GOTO that doesn't include the "goto" keyword.
Overlooked the return in my third if statement, but you get the idea....
I want peace on earth and goodwill toward man.
We are the United States Government! We don't do that sort of thing.
It turns out that many CPUs have hardware support for comefrom, usually to support debuggers. For instance, x86 has DR0, DR1, DR2, and DR3. And that's the only place I'd use a comefrom: as a debugging breakpoint.
Goto is easy to avoid, and is a symptom of a poorly designed program.
[...]
I have occasionally used goto as a quick hack, usually to handle error conditions.
In languages with exceptions, one uses a finally clause (or a C++11 scope guard) to perform cleanup, such as releasing memory or non-memory resources that a function owns. In C, one uses goto to jump to the cleanup code if it detects an exceptional condition. Or is a program that needs to do this sort of cleanup "poorly designed" solely on account of being implemented in C instead of a language with language-level exceptions?
When I later go back and refactor the code, it is always cleaner and more readable without the goto.
By "refactor" do you mean "rewrite in C++"? If not, could you give an example of such a refactoring?
I find the gotos easier to read than the nested set of if statements. Especially because the gotos are a well known idiom for handling this type of error/cleanup.
That's horrible with the code duplication. It gets even worse if the cleanup is 2 or 3 lines per resource.
The key virtue, such as it is, of the do { ... } while(false), better wrapped up in a macro, is that it makes it harder to get the nesting wrong by accident, which is critical.
With goto there is no such safety net.
But I think we're agreeing furiously.
Rgds
Damon
http://m.earth.org.uk/
So abuse loops and nesting when you have no intention of looping? And use break which is just goto lite?
That always seems like standing on one's head to please the compiler.
is far more entertaining than a mere goto.
"To those who are overly cautious, everything is impossible. "
This is simpler, cleaner, and avoids the goto:
void func() {
if (AquireResource1()) {
if (AquireResource2()) {
if (AquireResource3()) {
DoStuffWithResources();
CleanUp3();
}
CleanUp2();
}
CleanUp1();
}
return;
}
I've seen 3 or 4 attempts to make the nested logic correct and/or readable. The statement with the goto's was easy to understand. Immediately. I think it proves the point that sometimes goto's have a place, and actually may be a better solution.
His ignorance covered the whole earth like a blanket, and there was hardly a hole in it anywhere. - Mark Twain
If AcquireResource1() fails, you have unitialized entries in resources[1] and resources[2].
And this array only works if your resources all look the same.
Return from subroutine is the same as popping a function from the stack and tail calling it. "Comefrom" is a different construct, stating that whenever some other line of the program gets executed, a particular function will get called first. Have you heard of "aspect-oriented programming"?
In the 60's and much of the 70's, most people wrote in high-level languages as if they were coding assembler. Goto's all over the place. Not that they had a choice -- for example, control flow in Fortran IV, the most-used high-level language of the time, featured IF, DO (a crude version of the modern FOR -- not do), GOTO, CALL, RETURN. No else, while, do/while, no modern-style for, case, etc. AND, get this: NO BLOCKS; the IF statement controlled only a *single* statement, so that meant you often *had* to say IF (...) GOTO xxx. Just like assembler. It was awful! There were other less-popular but more-evolved languages, but unstructured practices were very often carried over to those as well. GOTOs were just how most programmers thought.
That's the backdrop for Djikstra's condemnation of GOTO. Certainly, the then-current mass use of GOTOs was a very bad thing since it completely obscured program logic. If you read the original article, he's not so much condemning GOTO as he's arguing for structured programming.
Consider GOTO Considered Harmful as a successful wake-up call. By keeping his message black/white, i.e. GOTO is bad, he gave his message punch and made it much talked-about. People started to think in a more structured manner (though at first we thought the "structured crowd" were a bunch of weenies), and started to demand better control-flow features. Pretty soon, structured control-flow was de rigeur in any new or revised language. Fortran even got IF/END IF in Fortran 77!
People nowadays have hardened the anti-GOTO bias into gospel. At the time, the response was more nuanced, more in line with the spirit of what Djikstra was saying. For example, in 1974 even Niklaus Wirth's new PASCAL (a principled, hard-line structured language if there ever was one) included the goto statement with the warning in the User Manual and Report that "the goto statement should be reserved for unusual or uncommon situations where the natural structure of an algorithm has to be broken." If anybody was going to out-and-out outlaw goto, Wirth would have been the guy.
What I would have loved to see in C would be a different keyword for break for exiting a loop versus ending a switch case. 90% of the times in recent years that I've been tempted to use goto have been when I've written a switch statement in a loop, and need to break out of the loop from one of the cases. I have to steel myself and either rewrite the particular case as an if statement before the switch (nasty), or fiddle around with flags to break out of the loop after the switch statement, or check it as part of the loop condition.
People forget that Djiksrtra wrote his famous missive back when the dominant languages were PL/1 and Fortran and goto was the main mechanism for flow control.
Dijkstra's point was perfectly sensible and a valid at the time. I'm just not sure that it deserves to be elevated to the status of Eleventh Commandment.
Don't let THEM immanentize the Eschaton!