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/
FART: printf("FART\n"); goto FART;
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?
If we're now allowed to have GOTO, can we have ALTER as well?
PLEASE!
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.
If you feel you "need" to use GOTO to make things easier, then you should take a second look at your code and invest a little time figuring out how best to reorganize your code. GOTO gives an easy out for lazy programmers who don't want to invest the time to give a little thought on how they programmed themselves into said box. In my 25 years of software development, I have never found a situation where the use of GOTO would produce better code than putting a little thought into your code.
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.
(Warning: Olde Phart story here.)
Years ago in a previous life, I programmed in FORTRAN III and IV, with lots of GO TO statements. Later, when I had access to FORTRAN 77 with Vax extensions, the need for GO TO statements went away. So I took one of my programs (~2000 lines, IIRC) and rewrote it using the "GO TO" - free logic, and lo and behold I didn't have to move or rearrange even ONE line of code.
Folks, it's not the GO TO that is the problem, it's sloppy and disorganized thinking that leads to spaghetti code.
(All you kids, GET OFF MY LAWN!)
void func() {
if (!AquireResource1()) goto end;
if (!AquireResource2()) goto cleanup1;
if (!AquireResource3()) goto cleanup2:
DoStuffWithResources();
Cleanup3();
cleanup2:
Cleanup2();
cleanup1:
Cleanup1();
end:
return;
}
Apple devs, get in here!
That result's because programmers got the ideas in "Goto Considered Harmful" pounded through their skulls while they were learning, and handled it like they would dynamite: it's very effective and the best tool for certain jobs, but it's also very dangerous and capable of causing a ton of damage so you should handle it with an abundance of caution. tl;dr: "Use it to crack huge boulders and tree-stumps, not to loosen bolts."
No it's not. Bad programmers, like you, blame the language features for their crappy code.
"GOTO" existed before subroutines and functions were added, and it was back in the days of line numbers. This was the point where the main menu part of a program had to jump over to the appropriate part of the program, now we just call the appropriate routine.
Headline should read "Thanks to Dijkstra's warning, GOTO in practice not harmful."
Support the First Amendment. Read at -1
Some sort of nested do { ... } while(false); and break; would be my first suggestion.
Rgds
Damon
http://m.earth.org.uk/
It's even worse, but it avoids the word "goto" (while still being a goto IMHO):
while(1) {
if (!AquireResource1()) break;
DoStuffWithResources();
break;
}
Cleanup();
W..w..W - Willy Waterloo washes Warren Wiggins who is washing Waldo Woo.
Goto messes with readability and long term maintenance. Anyone who uses Goto is probably going to use it correctly. Otherwise you wouldn't use it. But you use it to construct loops, and functions which is unnecessary and illegible compared to every other option.
Cleanup resources using destructors, use a plain return on failure.
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.
Destructors? This isn't C++.
void func() {
if (AquireResource1()){
if (!AquireResource2()){
if (!AquireResource3()){
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
return;
}
"Cleanup resources using destructors"
Good luck with that.
void func () {
if (!AquireResource1()) return;
if (!AquireResource2()) {
Cleanup1();
return;
}
if (!AquireResource3()) {
Cleanup2();
Cleanup1();
}
DoStuff();
Cleanup3();
Cleanup2();
Cleanup1();
return;
}
I want peace on earth and goodwill toward man.
We are the United States Government! We don't do that sort of thing.
Fundamentally, that *is* a GOTO. It's just a less-readable idiomatic GOTO that doesn't include the "goto" keyword.
I wish I could show you some code that I and others had written in the late sixties and early seventies. The term "Spaghetti Code" was invented to describe the flow charts that were made to document the code. Those charts actually looked like a bowl of spaghetti. There's nothing like it today.
Maintaining code like that was some of the most tedious and frustrating (and sometimes fruitless) work I've ever done.
That all changed with the invention of structured programming (which, btw, does NOT mean "top-down modular"). Today, everyone writes structured code, though they don't know it. But at the time, it was revolutionary. There were even reactionaries who folded their arms and staunchly refused to ever code that way.
When I first learned it, my whole world view changed. Coders were walking around with glassy eyes, amazed that the world had changed so quickly. You have no idea.
Structured programming was the first systematic rejection of goto statements. But even then, it wasn't total. Gotos were still allowed for error conditions. They said, "When there's an error, anything is allowed".
OKsofar
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.
Okay, then I'd recommend C++.
It's so close to a strict superset that I don't think that's an unreasonable response.
Similarly stupid C/C++ dogma:
Macros are dangerous.
Function pointers are unsafe (MISRA).
Multiple inheritance is bad.
Heap allocation is unsafe.
Actually you could split off each resource acquisition into separate function that does acquire -> check -> cleanup cycle for one resource and calls next function in chain. Not shorter than goto way but you're less likely to shoot yourself in the foot by messing up order of cleanups or something.
Comment.c:12: Warning missing return before }
I mean this.
void func() {
if (AquireResource1()){
if (AquireResource2()){
if (AquireResource3()){
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
return;
}
Really this is a COMEFROM statement, no?
bool OnRightButtonClickCallBack(void *a, void *b, void *c){
int xx=int(a);
int yy=int(b);
global_window *gw=(global_window *) c;
}
sed -e 's/Chuck Norris/Rajnikant/g' joke > fact
Wasn't it Vint Cerf who redacted "A case against GOTO" to the phrase we all know?
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?
Some/many languages in 1968 didn't have 'modern' control structures like while() for() etc. Or the ability to group lines of code. In this case it's not really the goto that is bad, it's the lack of features that is bad. This is so obviously bad it's not worth commenting on.
The real reason Dijkstra hated goto is it made it impossible to write 'proofs' about programs. IE, prove the program or algorithm is correct. So his hope was by restricting the grammar programs so written could be mathematically proved. Pascal I believe was also written with the idea that formal proofs were important. Suspect Java followed on the basis of cargo cult compiler design. No goto or unsigned integers for you.
Either way, except in narrow cases, such proofs are essentially impossible. And computer scientists gave up around 1980. However the cargo cult still exists telling generations of programmers that goto, continue, and multiple returns are shameful.
// This especially becomes important when you have many nested loops // I want to break out of all loops here. // I could make a variable to incrementally // break out of the loops one by one like so // or I could do the following and get rid of the endloop variable completely: // Rest of code
bool endloop = false;
while(condition())
{
if(endloop) break;
while(condition2())
{
if(endloop) break;
while(condition3())
{
if(condition4())
{
endloop = true;
break;
goto exitloop;
}
}
}
}
exitloop:
"What would you propose as a better alternative..."
A switch/case statement, or function calls.
Using "goto" is akin to using global variables, imho.
Political correctness is really just herd psychology pushed by insecure people who desperately seek social conformity.
Because if you use goto in a class assignment, you lose points.
If the assignment is "Implement a coroutine mechanism for C", why would a sane instructor dock the student for making something like this clear wrapper around goto ?
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.
Fundamentally, perhaps, but in actuality, no.
If there is no goto keyword then there is no goto usage.
Political correctness is really just herd psychology pushed by insecure people who desperately seek social conformity.
What would you propose as a better alternative to this idiom in a language that lacks exceptions:
I propose this; namely using variables to keep track of the state of resources, and then cleaning up based on the values of those variables. In my experience this is much less error-prone than the "goto" equivalent - for example, reordering the code is much less likely to break the cleanup.
Need to type accents and special characters in Windows? Use FrKeys
That's horrible with the code duplication. It gets even worse if the cleanup is 2 or 3 lines per resource.
I think one is a fool for not using available commands just because they can be misused. There's absolutely nothing wrong with a goto in and of itself, only the logic issues it may cause in improper use. Sure, you may not like goto and you are free not to. But to make all this noise about it's existence is plain silly.
Kind of like how some people stick with the original version of vi because of some fear of change, or whatever drives them. They are free to but to oppose improvement rather than just ignoring it is also silly. Not everyone want to be stuck having to press i for insert every time the cursor lands in the first column.
Now if you don't have much of a life with enough challenges ...
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/
Fixed...
void func() {
if (AquireResource1())
{
if (AquireResource2())
{
if (AquireResource3())
{
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
return;
}
There are places to use goto, but this isn't one of them
void func()
{
if (AquireResource1())
{
if (AquireResource2())
{
if (AquireResource3())
{
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
return;
}
I know some people are allergic to triangles, but I like them because it makes it easy to see what code is potentially skipped and which code is definitely going to run (barring a crash) at any given time.
Another alternative:
void func ()
{
Resource resources[3];
if (AcquireResource1(&resources[0]) &&
AcquireResource2(&resources[1]) &&
AcquireResource3(&resources[2]))
{
DoStuff(Resources);
}
ReleaseResources(resources);
return;
}
This is essentially simulating RAII semantics in C. You might even pack all three AcquireResource[1|2|3] functions into a common AcquireAllResources and in so doing make the if less ugly.
I don't like that goto code above because I think it makes it difficult to follow every flow of execution. You may be used to it and think the opposite.
What kind of ALTER statement are you imagining?
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.
A good case is where you need to break out of a complex block of code and do some clean up before the return. Like freeing memory or file handles. I also recently had a case where goto really simplified a bunch of nested if statements. Another case is inside state machines build around a switch() statement.
You can have.
CASE_BLAH:
label_blah;
The main reason for that is to get rid of complicated duplicate code. Mainly because having two bits of code that should be identical, but isn't quite results in maddening bugs.
In general I think, if the goto it tranfering control forward it tends to be 'okay' goto's transferring control backwards is suspicious.
FART: printf("FART\n"); goto FART;
OK fine do it this way if you are using BASIC or some other language where GOTO is a common idiom.
But in most common procedural languages something akin to
while (true) do print("FART\n") ; done
or
do print("FART\n") until (false)
is easier to read, if only because you don't have to remember that lines can have labels.
Knowledge is how to play a game, intelligence is how to win, wisdom is knowing what game to play.
Am I missing something?
void func()
{
if (AquireResource1())
{
if (AquireResource2())
{
if (AquireResource3())
{
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
return;
}
is far more entertaining than a mere goto.
"To those who are overly cautious, everything is impossible. "
Okay, then I'd recommend C++.
So long as your target machine is big enough to support C++'s support library. People used to joke about "Think of the elevator controllers!" as an excuse people give to hold up language design. But it's no joke anymore, as Arduino's MCU kits brought programming for a constrained machine to the masses.
if (AquireResource1()) {
if (AquireResource2()) {
if (AquireResource3()) {
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
return;
}
Isn't this a textbook example of unwounding a stack?
Forget magic. Any technology distinguishable from divine power is insufficiently advanced.
// I wouldn't go so far as to claim this is "better" than the goto version.
// Especially since "goto cleanup" is a well-known idiom.
// However, I do think this reads quite naturally.
void func() {
bool hasResource1, hasResource2, hasResource3;
hasResource1 = AcquireResource1();
if (hasResource1) {hasResource2 = AcquireResource2();}
if (hasResource1 && hasResource2) {hasResource3 = AcquireResource3();}
if (hasResource1 && hasResource2 && hasResource3) {
DoStuffWithResources();
}
if (hasResource3) {Cleanup3();}
if (hasResource2) {Cleanup2();}
if (hasResource1) {Cleanup1();}
return;
}
You can't just grab a handful of lines of code and make it a separate function. A function needs to have a clear purpose, not too many arguments, and preferably not more than 1 return value.
Pass your post through a prettifier and see how the arrow anti-pattern becomes clear.
This is simpler, cleaner, and avoids the goto:
void func() {
if (AquireResource1()) {
if (AquireResource2()) {
if (AquireResource3()) {
DoStuffWithResources();
CleanUp3();
}
CleanUp2();
}
CleanUp1();
}
return;
}
A switch/case statement, or function calls.
Can you demonstrate that these improve readability of the program in all cases?
I hate that code. Precisely because it is so easy to overlook something, like you did. And even easier for a maintenance programmer coming later to overlook something.
"First they came for the slanderers and i said nothing."
Absolutely. It took me about 5 seconds to understand the snippet with the goto's. The one with brackets is just ugly, though indentation would have made it clearer and easier to match up the brackets (which of course, the OP may have done and /.'s editing tools failed to render). I imagine that a bracket mismatch could provide for some debugging hilarity, just as an errant goto. Both get the job done, though. As long as the goto logic is nice and tight, it can provide for both hard- and meat-ware efficiency.
His ignorance covered the whole earth like a blanket, and there was hardly a hole in it anywhere. - Mark Twain
Good point. A goto statement wouldn't violate Don't repeat yourself (DRY) as much as Shakrai's suggestion.
The army of embedded C developers standing behind you with torches and pitchforks begs to differ.
As others have show, you can use nested conditionals, status variables, and/or other means to avoid using Goto.
Yes, conditionals, case: statements, and similar constructs are really "goto" statements under the hood, but they are generally easier for humans to read and maintain and easier for compilers to optimize than a goto statement.
Which of the options listed below should you use? In many cases that will depend on the "coding style" of the developer or development team rather than any minor differences in "technical merit."
Knowledge is how to play a game, intelligence is how to win, wisdom is knowing what game to play.
Actually you could split off each resource acquisition into separate function that does acquire -> check -> cleanup cycle for one resource and calls next function in chain.
I hope you made lots of spaghetti.
Not shorter than goto way but you're less likely to shoot yourself in the foot by messing up order of cleanups or something.
Its lack of readability means you may end up shooting yourself in the foot when trying to understand this spaghetti seven months later when you have to make a change.
To be honest I've never understood the hate towards GOTO. In assembly language all you are doing is telling the computer where to go in order to do the next thing. In fact the program counter is a register just like any other which you modify to move to another statement, and at the very least increments by one each fetch cycle. Each and every pointer is telling the computer where to GO TO in order to find a certain variable. Each and every RETURN is an implicit goto. IF is nothing more than Branch On Equal. In fact the only thing you are doing with a computer is telling it where to take its next data and its next instruction, which at the very least is a GOTO NEXT BYTE. To fear GOTO is simply to fear the Von Neumann machine.
Many program languages are just abstractions to try to encapsulate in some way the place or time that something is supposed to be done. But in the end, it is just a glorified GOTO statement.
GOTOs are the only reality. If you fear GOTO, maybe there is something wrong with your philosophy, or are trying to abstract away from the problem too much.
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
What horrible error-prone hard to follow code. Fair enough, it looks okay for this trivial example with only three pieces of logic in it, but real-life code has more than three pieces of logic in them. Look at this code from this library over here, line 85, function xp_new_xcfg() (unfortunately /. won't let me post the actual function code - "please use fewer junk characters").
Using your multiple-block example this short 12 line function becomes a 30-odd line monstrosity that is harder to read, much more error-prone to write and extremely difficult to debug due to redundant lines, yet this is still a trivial, rather short function! Can you imagine the mess that would occur if you tried to convert a more substantial function with error-handling goto's into your multiple-block version?
We were all taught the multiple-block approach to avoiding goto's when learning programming in university/school so it's not like none of us goto-using ancients ever tried it before. The problem with that approach occurs when a codebase is touched by many coders. They may not be as adept at making sure, when they modify that multiple-block version, to (for example) free the previous five pointers in the 6th failure handler "if" statement. Then we get a memory leak.
The goto version greatly reduces the probability that someone, when adding a new line of code with error-checking into the function, will forget how many foobars were allocated prior to that line and free too few in their error-handler "if" block. All they have to do is ensure that they jump to the handler that free's all.
I'm a minority race. Save your vitriol for white people.
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.
:)
// Ideally, you'd use smart-pointers for the resources, but the example assumes the language doesn't provide for them. // For three resources, I'd do the following, but for much more than that, I'd start using for-loops and arrays.
void func() {
int acquiredResource1 = FALSE, acquiredResource2 = FALSE, acquiredResource3 = FALSE;
acquiredResource1 = AcquireResource1();
if (acquiredResource1)
{
acquiredResource2 = AcquireResource2();
}
if (acquiredResource2) // Only need to check for acquiredResource3, but checked for all for the sake of readability
{
acquiredResource3 = AcquireResource3();
}
if (acquiredResource1 && acquiredResource2 && acquiredResource3)
{
DoStuffWithResources();
}
if (acquiredResource3)
{
Cleanup3();
}
if (acquiredResource2)
{
Cleanup2();
}
if (acquiredResource1)
{
Cleanup1();
}
}
When you have 5+ resources, then your example is not cleaner.
Having to scroll right to see anything but white space from indenting is not clean code.
Goto these days is a stenography tool
Agree. It makes the code shorter so that it can be written quickly and ideally read and understood quickly as well. Eliminating some of the fluff that we discussed recently helps someone read the program by keeping more functionality on the screen at once.
it's designed to make the code harder to read.
Disagree, when used by a competent programmer.
But it doesn't work, because you didn't initialize hasResource2 and hasResource3 variables to false.
Something closer to this?
void func () {
for(int i=0; i if(!AquireResource(i)){
Cleanup();
return;
}
}
DoStuff();
Cleanup();
return;
}
Comment removed based on user account deletion
// You mean like this?
// I don't mind it, but I have worked with people who would hate this style.
void func3() {
if (AcquireResource3()) {
DoStuffWithResources();
Cleanup3();
}
}
void func2() {
if (AcquireResource2()) {
func3();
Cleanup2();
}
}
void func() {
if (AcquireResource1()) {
func2();
Cleanup1();
}
}
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"?
Why? My point has been made.
Political correctness is really just herd psychology pushed by insecure people who desperately seek social conformity.
Mostly Harmless.
There are serious bugs in your attempt.
Both hasResource3 and hasResource2 are tested in the cleanup portion without having been initialized when at least one of of AcquireResource1() or AquireResource2() fails; they'll have some undefined value from the stack and Cleanup3/2() will be called inappropriately. This is true in both C, which has no 'bool' or C++, which does have bool, but won't automatically initialize it when auto.
The goto version does not involve creating the bool state variables and so avoids this subtlety. That makes the goto version inherently better, in my opinion.
Maw! Fire up the karma burner!
Man, you just blew another coding rule of thumb....
There should be only ONE return statement in a function, especially when said function doesn't have a return value...Exceptions to this should be CLEARLY commented to not confuse those who maintain your code.
"File to fit, pound to insert, paint to match" - Aircraft Maintenance 101
What would you propose as a better alternative
A switch/case statement, or function calls.
Can you demonstrate that these improve readability of the program in all cases?
Why? My point has been made.
Because I disagree with you that your "point has been made" until you have shown how it is "a better alternative".
Am I missing something?
Taste.
Maw! Fire up the karma burner!
Did we already forget?
https://www.imperialviolet.org...
int err;
if ((err = PrepareHash(x,n)) != 0)
goto fail;
goto fail;
if ((err = CalculateHash(x,n)) != 0)
goto fail;
return CompleteHashx,n);
fail:
return err;
"GOTO" should never have been allowed in C.
Yes, it should. But that doesn't mean you should use it.
You obviously have much more time on your hands.
Do you realize how absolutely ludicrous that is? Demonstrating readability in "ALL" cases?
Sorry, but I have better things to do than entertain naysayers.
Political correctness is really just herd psychology pushed by insecure people who desperately seek social conformity.
From a very superficial analysis, I'd say the fact that Dijkstra, among others, mentioning it back in the day really influenced the practice nowadays. That is yet another consequence of the enlightened influence the most knowledgeable imprint in society over the ages. The study is surely good science. Nonetheless, good science is only as good as the context it is applied, so the conclusions, even though interesting, must be made and taken with a grain of salt. This is because pretty much like testing human aggressiveness during primitive periods and testing it now would obviously provide the most disparate results in multiple orders of magnitude, and the same would have happened with this GOTO theme if there was this parallel universe we could compare - one where Dijkstra and co. hadn't chimed in about the subject, and nobody else stepped in to replace them. The greatest conclusion I personally take here is that such a large amount of code was influenced with the good practice evangelized by Dijkstra, and the pondered decisions that were made about the subject. Pretty much the same reason why IT is paying more and more attention to things like QA, agile, documentation, process, workflow and integration - we get these enlightened, experienced entities (individuals and companies alike) showing us just how important they are, and we apply those concepts with assurance that they will lead to better results.
You seem to classify people into two categories Pendant and not Pendant. I am sure many people are partial pendants.
I had to write code that used MISRA standards and even had the standards worked into compiler to generate errors so you couldn't just ignore the stupid.
It took me about 5 minutes reading through the MISRA standards to figure out that it was written by a committee of bureaucrats who don't actually know how to write software. Sure, there were some good things in the standard to improve readability and things like always using braces with every conditional so you can avoid problems like the Apple "goto fail" screw up. But several standards showed a complete lack of understanding about the definition of the C language. According to one of their rules, you couldn't test for a null pointer and then use the pointer in the same statement. The most notable example I can think of was the very common place ( && ) construct. Even though the && operator is a shortcut operator that absolutely checks the left hand side and kicks out if it is false (i.e. when the pointer is null), the brain trust at MISRA wouldn't let you do that. The language is defined to work correctly and you can't call yourself a C compiler if you break functionality like that. But no, MISRA standards won't let you do that because it "might" cause you to dereference a null pointer. It's a perfect example of good ideas being taken way too far and then turned into rigid dogma run amok.
Coding standards are good. Encouraging (but not requiring) people to not use GOTO unless they really have to is a "good thing". When you can't use C++ but you've designed your system to mimic destructor behavior and "exception" like error handling (i.e. to mimic RAII behavior), there are times when it's hard to do it any other way. The C language just doesn't give you the tools you need to implement OOD any other way.
I've been programming for over 35 years now and I remember the disgusting mess that you found yourself in with GOTOs sending yourself all over hell and gone. So I understand what Dijkstra was warning us against. And he has a point. You don't want to use GOTO as your only form of flow control. It quickly devolves into an unreadable mess. But pretty much every language these days has conditionals and loops to keep programs well organized and everyone knows how to use them properly. The GOTO is left to times when it's really necessary, as it should be. Because sometimes it really is necessary.
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.
Sure indentation can help. But at the last place I worked they would have so many nested if statements that it would indent all the way across the screen. It was quite hard to keep track of which set of braces you were inside of. I don't know if goto's would have helped in their case, but too many nested groups definitely hurts readability.
-- ssoorrrryy,, dduupplleexx sswwiittcchh oonn.. -Quote found on actual fortune cookie.
If you are three levels of brackets deep before your important code even starts, you're doing it wrong.
I think my personal record is 27 layers of curve-and-angle brackets in, while doing some intricate bitmap manipulation - but that's largely because I like to make sure all my arithmatic is unambiguous even for those who can't follow the order of operations. I often end up with something like total+=bitmap[(x+offx+(width*(y+yoff)))*3]+bitmap[((x+offx+(width*(y+yoff)))*3)+1]+bitmap[((x+offx+(width*(y+yoff)))*3)+2]. It's pretty ugly.
You could do the following, and with a little non-standard indenting, it's rather more elegant, I think.
void func() {
if (AcquireResource1()) {
if (AcquireResource2()) {
if (AcquireResource3()) {
DoStuffWithResources();
Cleanup3;
}
Cleanup2;
}
Cleanup1;
}
}
Put my fist through my alarm clock with its ding-dong death inside my ear. - The Blackjacks.
void func() {
bool haveResource1 = AquireResource1();
bool haveResource2 = AquireResource2();
bool haveResource3 = AquireResource3();
DoStuffWithResources();
if (haveResource3)
Cleanup3();
if (haveResource2)
Cleanup2();
if (haveResource1)
Cleanup1();
}
Well, to me, this is even more clear:
(defun func ()
(with-resource1 (x (acquire-resource1))
(with-resource2 (y (acquire-resource2))
(with-resource3 (z (acquire-resource3))
))))
with the "with-" macros handling any local bindings and/or cleanup for the resource. It's a well-known pattern.
That is all.
I'm a fan of early returns.
void func() {
if (!AquireResource1()){
return;
}
if (!AquireResource2()){
Cleanup1();
return;
}
if (!AquireResource3()){
Cleanup2();
Cleanup1();
return;
}
DoStuffWithResources();
Cleanup3();
Cleanup2();
Cleanup1();
return;
}
if (!v) ...
goto out;
more work
out:
It makes it so much cleaner and easier to read.
What problem did you solve, indenting a conditional block of code?
How is it more readable to mask / hide the fact that a block of code is conditional?
http://pastebin.com/wMeeMKVb
Just sayin'
No GOTO? No problem!
void func() {
if (!AquireResource1()) fail1: {}
if (!AquireResource2()) fail2: {}
if (!AquireResource3()) fail3: {}
DoStuffWithResources();
Cleanup3();
comefrom fail3;
Cleanup2();
comefrom fail2;
Cleanup1();
comefrom fail1;
return;
}
Wait, C doesn't have COME FROM?! crap. Time to alert the C standards body!
http://pastebin.com/wMeeMKVb
Just sayin'
Thanks for the attempt - it illustrates the problem quite nicely. If any of the xstr_dup calls fail you leak memory. The original one I linked to will not leak memory no matter which line fails.
I'm a minority race. Save your vitriol for white people.
int i;
bool ok = true;
for (i = 0; ok && i < 3; i++) {
; switch (i) {
; ; case 0: if (!AcquireResource1()) ok = false; break;
; ; case 1: if (AcquireResource2()) ok = false; break;
; ; case 2: if (AcquireResource3()) ok = false; break;
; }
; if (!ok) break;
}
switch(i) {
; case 3: Cleanup3();
; case 2: Cleanup2();
; case 1: Cleanup1();
}
Why yes, I read thedailywtf.com, why do you ask?
#naabhaprzrag, #sverubfr-000, #agi-fcbafberq, negvpyr[pynff*=' negvpyr-ary-'] { qvfcynl: abar !vzcbegnag; }
minor change:
case 2: if (AcquireResource3()) ok = false; else DoStuffWithResources(); break;
#naabhaprzrag, #sverubfr-000, #agi-fcbafberq, negvpyr[pynff*=' negvpyr-ary-'] { qvfcynl: abar !vzcbegnag; }
It's called the DRY principle, as in Don't Repeat Yourself. Any time you do the same sequence of stuff in multiple places, when that sequence needs to change, the chance of missing one is increased.
#naabhaprzrag, #sverubfr-000, #agi-fcbafberq, negvpyr[pynff*=' negvpyr-ary-'] { qvfcynl: abar !vzcbegnag; }
C was a system implementation language, which meant that it needed very basic constructs. "goto" is one, and the ability to read and write to specific pieces of memory is another.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
There should be only ONE return statement in a function
Um, yeah, I'm gonna have to put up a [CITATION NEEDED] here. Sure, that's a thing, but so is Systems Hungarian Notation.
#naabhaprzrag, #sverubfr-000, #agi-fcbafberq, negvpyr[pynff*=' negvpyr-ary-'] { qvfcynl: abar !vzcbegnag; }
Is pretty useful.
No, he's objecting to the example offered as an appropriate use of goto. It wasn't an appropriate use. It fails at the stated goal, making the code more readable. It makes it less readable by avoiding the indentation of conditional code.
If there had been a different example with nested code at various levels bailing out with goto's then maybe it would have been appropriately used, not necessarily but maybe. However as offered the example was a good example of when *not* to use a goto.
void func() {
int bail = 3;
if (!AquireResource1()) bail = 0;
if (!AquireResource2()) bail = 1;
if (!AquireResource3()) bail = 2;
DoThings();
switch(bail) {
case 3: cleanup3();
case 2: cleanup2();
case 1: cleanup1();
}
return;
}
But of course, taking advantage of switch fallthrough is nothing but a translucent fig leaf over a bunch of gotos. I.e., very useful to have in mind if you're dealing with a Religious Fundamentalist who hates goto No Matter What.
There are times you have to use goto. I once needed unknown-depth recursion in CUDA but __device__ functions are verboten from calling themselves (nvcc needs to know stack size at compile time so it knows how many resources to allocate at launch). But I needed unknown-depth recursion (even if I had a good idea what the max would be) so I #defined a MAX_DEPTH and put a variable called "stack" to track my paths through the octree and I used GOTO to loop over a section of code an unknown number of times. The code had to traverse an octree with up to ~10s of millions of elements at the final level, determining a union of subsets of each level which exactly covered the 3D space it represented to perform a weighted sum over them (it computed the integral solution of the Poisson equation at an arbitrary point).
I won't pretend it was pretty but oh lordie lord did it ever zip along... Surprisingly the process is amenable enough to Matlab's vector/set notation that I got the .m version to run fully half as fast as the compiled .c version, which itself was about 1/10 as fast as the .cu version.
If you are three levels of brackets deep before your important code even starts, you're doing it wrong.
Perhaps. But the real problem is bad design of the functions being called. The code should look like this:
void func() {
int resources = R1 | R2 | R3;
if (acquireResources(resources)) {
doStuffWithResources();
releaseResources(resources);
}
}
This way the error handling, and sequencing of acquiring and releasing, are completely separated from your code flow, and buried in a subroutine that can be reused whenever needed. You may be able to come up with a contrived example where "goto" seems appropriate, but that is usually a symptom of bad design on another level.
Use a single generic cleanup that only cleans up what it has to clean up. That way you don't have a difference between the regular code path and the exception path. Also keep track explicitly on what you have allocated and what you haven't.
int func(void)
{
int something = E_NORESOURCES;
ResourceHandle handle1 = NULL;
ResourceHandle handle2 = NULL;
ResourceHandle handle3 = NULL;
handle1 = GetResource1();
if ( ! handle1 ) goto out;
handle2 = GetResource2();
if ( ! handle2 ) goto out;
handle3 = GetResource3();
if ( ! handle3 ) goto out;
something = DoSomething(handle1, handle2, handle3);
out:
if ( handle1 ) Release(handle1);
if ( handle2 ) Release(handle2);
if ( handle3 ) Release(handle3);
return something;
}
RAII and modern C++ get rid of this error-prone coding style. The shared_ptr and unique_ptr template classes, with custom deleters if necessary, can remove all of these contortions and prevent someone from adding a return in the middle of the method that would lead to resource leaks..
OK so I refactored again and it's getting messy :)
http://pastebin.com/geq6C1rg
I DO see the utility of GOTO statements - I grew up with 10 Print "Hello", 20 Goto 10, so it always has a place in my heart lol.
Just trying to play devils advocate here, and learning some along the way. Just a casual programmer here.
...is that consistent indentation makes code vastly easier to read and much harder for bugs to hide in; I imagine pretty much everyone nowadays uses auto-indentation when writing C code, which if course is curly-bracket based. If you use "GOTO" suddenly your indentation doesn't match your program flow; it's asking for trouble.
For the same reason I try to avoid doing a "RETURN" in the middle of a function where reasonable (if I need to bail due to an error etc I try to do it right at the start of a function before any of the meat)
That just leaves "BREAK" as a program-flow-modifier and I can live with that just fine.
I don't think I've _ever_ used GOTO in several decades of programming C.
[FrLz]
The academics are always on a quest for purity, people who work for a living want to get things done as cleanly and quickly as possible.
Banging out code quickly during initial development is rarely getting things done quickly in the long run. One must also consider maintenance and future updates and enhancement, often made by someone other than the initial developer, which is where time gets lost due to mistakes during initial development. Many have good intentions of going back and cleaning up initial code but actually getting a chance to do so is not as common as many wish.
The general idea is that misuse of goto leads to buggier code, and that bugs are less expensive the earlier they are found, and ideally avoided in the fist place by good coding practices. So banging out code quickly can be counterproductive.
As for cleanly coded, what is cleaner and more readable about masking that fact that a block of code is conditional by avoiding indentation, which is all that the offered goto example is doing. Perhaps if the example had bailouts from several levels of nesting, but even in such situations goto is not necessarily the best way to go.
error1 |= AquireResource1();
error2 |= AquireResource2();
error3 |= AquireResource3();
if((error1 == FALSE)
&&(error2 == FALSE)
&&(error3 == FALSE))
{
DoStuffWithResources()
}
else
{
if(error3 != FALSE) {Cleanup3;}
if(error2 != FALSE) {Cleanup2;}
if(error1 != FALSE) {Cleanup1;}
}
And this isn't even the best alternative option to your goto, but your example was more difficult to read, and you are assuming that the labels will stand out.
I also don't see why you would just have one cleanup call. If you were THAT stressed on runtime, then perhaps goto would be better, but it also depends how the compiler interprets this anyways.
Yeah, now imagine how messy it gets when you have to free each of the results from xstr_dup individually and not miss any - turn this function into one without goto's.
It gets worse actually - this example was structured purely to make error handling easier. Some error handlers call functions to free resources that can't take NULL (fclose, for example). This trivial function uses free(), which can take NULL so we don't have to check if each variable is non-NULL before we free it. If those were file handles we'd have to check them for less than zero before we can fclose them.
I'm a minority race. Save your vitriol for white people.
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!
It is not the GOTO that is the problem, it is the label that is the target.
When examining a piece of code (not written by you) and seeing a goto it is quite obvious what will happen at that point. The flow of logic can be easily worked out. If, however, you are looking at some code and you see a label then the flow of logic can only be determined by examining the whole of the scope in which that label appears.
This reduces the localization necessary to identify how the program works.
The original programmer is unlikely to notice that this is a problem because in writing the code they understood what was happening and thus that there was a more limited scope.
Writing programs that the _next_ programmer can understand is an art that is seldom practised, especially among coders that think that goto is OK to use.
So much for proofreading. The ( && ) construct got cut out by the HTML parser. I'll retype it. It should read "the very common place ((NULL != ptr) && (-insert some user of the pointer here-)).
Easy.
http://pastebin.com/QV7KPuQS
Explicit stack unwinding in an error condition, checking exit point invariants and a single exit point are why most C functions should use goto's in the real world.
typedef struct
{
V v;
W w;
X x;
Y y;
Z z;
} Resources;
int Resources_init(Resources *r)
{
int ret;
if ((ret = V_init(&r->v)))
goto FAIL;
if ((ret = W_init(&r->w)))
goto FAIL_V;
if ((ret = X_init(&r->x)))
goto FAIL_W;
if ((ret = Y_init(&r->y)))
goto FAIL_X;
if ((ret = Z_init(&r->z)))
goto FAIL_Y;
assert(0 == ret);
goto END;
/* error handling and return */
FAIL_Y:
Y_fini(&r->y);
FAIL_X:
X_fini(&r->x);
FAIL_W:
W_fini(&r->w):
FAIL_V:
V_fini(&r->v);
FAIL:
assert(0 != ret);
END:
return ret;
}
Try to accomplish similar logic with nested if's and it will be garbage code. C++ has exceptions, constructors and destructors while C does not. You have to hand code the same logic unfortunately and check and handle every return for potential failure.
Please explain how else to handle error conditions sanely in C? For example, how do you cleanly implement a function that needs to acquire N resources where any one of the acquisitions can fail and on failure you need to clean up after yourself?
When you have 5+ resources, then your example is not cleaner. Having to scroll right to see anything but white space from indenting is not clean code.
Are you using a 40 column Apple II?
Without indent, it becomes really hard to determine which { goes with }, especially if you make the code a bit longer. And with indent, it quickly becomes too deep.
When Djikstra wrote his article, languages like C and Pascal with while() and do {} while() loops did not exist.
Gotos where very famous in Fortran and what ever was used at that time.
If a modern developer, regardles how 'young', abused gotoes in C or similar languages I would wonder why. It would be a clear sign he did not grasp the loop or conditional constructs properly. Note 'abused' not 'used'. However I'm not aware I ever needed a goto in the last 30 years, after I was done with Basic.
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
No Alpha ever listened to a word uttered by this self-promoting moron. Anyone who ever went to university to do any course connected with computers came across a lame-dog 'teacher' like Dijkstra. A puffed-up idiot so full of himself, he never noticed his skillset was atrocious, his inherent abilities almost non-existent (save for self-promotion and academic ladder-climbing), and his general knowledge of computing n years out-of-date where n = idiot's age - 20
It is the dribbling betas who chant "Duh- only bad programmers use GOTO, duh", just as they are trained- and actually for dribbling Betas, the advice is undoubtedly good.
Most programmers are BAD programmers, just as most people in any field of skill are BAD at that skill. But these bad programmers are the majority of working programmers. Their heroes are NEVER people like Knuth (an alpha), but people like Dijkstra who spends his life taking pot shots at programmers infinitely more talented the he. The old 'chip on the shoulder' psychology.
However, in a world of dumbed down shit like Win8/Win 10 'Metro', Dijkstra should feel in his element. This is the age of the rising moron, where functionality and sophistication are looked down on. Where everyone must use 'plastic scissors' because 'real' scissors might end up with someone cut or stabbed. GOTO, in a language, is like the point or shearing edge of metal scissors. A 'GOTO-free' language is the plastic scissors of programming.
C++ exceptions are commonly trotted out as the replacement for C's goto cleanup statement, and for desktop userspace, I might agree. Page 2 of your second link mentioned that exception support has a cost but didn't quantify it. Instead, it said "buy our e-book for $24.95". So all I have to go on is a personal anecdote, namely that the last time I tried adding exceptions to a C++ project that hadn't previously used them, it added 64K of code to the executable.
That idiom doesn't scale well. Here's a scalable alternative in Lua:
function Cleanup1() .... .... ....
function Cleanup2()
function Cleanup3()
function func()
cleanup = {} --stores all the cleanup functions to run
repeat
if not AquireResource1() then break end
table.insert(cleanup, Cleanup1)
if not AquireResource2() then break end
table.insert(cleanup, Cleanup2)
if not AquireResource3() then break end
table.insert(cleanup, Cleanup3)
DoStuffWithResources()
until true --never loops
--loops backwards calling each registered cleanup function
for i=#cleanup,1,-1 do
cleanup[i]()
end
end
It is more lines of code, but it's far less brittle. You can mess around with the resource acquisitions without worrying about keeping the cleanup code in sync. If for some reason you have a lot of resources/requirements, you can toss those functions into a table (paired with their cleanup function) and convert all those if nots into a loop.
while(1){printf("FART\n");}
if you're doing an init function you should match it with a deinit function.
// Cheating with C++ // lint intentional pass-through // lint intentional pass-through // lint intentional pass-through
So:
int initFunc() {
if (!AquireResource1()) return deinitFunc(TERM_1);
if (!AquireResource2()) return deinitFunc(TERM_2);
if (!AquireResource3()) return deinitFunc(TERM_3);
return 0;
}
int deinitFunc(state = TERM_ALL)
{
switch (state)
{
case TERM_ALL:
case TERM_3:
Cleanup3();
case TERM_2:
Cleanup2();
case TERM_1:
Cleanup1();
}
return -1;
}
I personally prefer the gotos but some people don't. I had to do the above pattern for something with a 24 step initialization process. Not all steps allocated resources of course but all of them could fail. The lead programmer hated gotos, so I had to do it that way. Which was also beneficial because I no longer needed to create a separate deinit function. To me it looks the same as gotos, *shrug* but to each their own.
I have used gotos in other cases. Mainly in driver development. I had something like 10 us between transmissions on a SPI bus and was trying to shrink that down. I replaced a continue with a goto and it saved me 90 ns. Made the code look horrible and ripped the goto out again to look for other ways to solve the problems. But sometimes when your optimizing and fighting for ns gotos are what's needed.
Software Engineer & Writer of Military Science Fiction and Fantasy Blog: petermwright.com Twitter: WrightPeterM
My use of goto sounds like everybody else's: if(something_bad_has_happened) goto code_to_fix_it;
In highly modular code with lots of procedures the "goto" may end up as a longjmp(). So be it.
...laura
I'd like you to show an example of goto refactored to switch/case and goto refactored to function calls, so that others can show a counterexample.
goto is open to abuse.
Rigid prohibition of goto is also open to abuse.
Which leaks all the resources that you had allocated prior to this check. The goto cleanup; idiom is intended to ensure those resources are deallocated. In this way, it's like the finally clause of a try block in Java or Python.
Deep nesting is not "cleaner."
So you have macros that transform a coroutine into a big switch statement, selecting on the source code line numbers on which yield statements appear, and putting all the function's local variables in a struct. I guess that's one way to do it. But how well does it work when there are switch statements within the coroutine itself?
Of course you can and you are supposed to do so.
For that the inventor invented public and private functions (and yes, that concept exists in C, too!)
Nearly all examples in this article I had refactored into multiple functions/methods, so had more or less any programmer I ever worked with.
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
What part of "rule of thumb" do you not get? There are times when it's done, but it should be avoided.
The reason is easy to get if you think about it. Functions start at the top and should end at the bottom... Returns in between are (generally) "bad form" because it's easy to miss them when looking at the source code.
BTW, this applies double when you are actually returning a value to the caller... Still there are exceptions, but in general, one return, at the bottom, is best.
Here is your reference... http://en.wikipedia.org/wiki/R...
"File to fit, pound to insert, paint to match" - Aircraft Maintenance 101
That never was a rule.
Someone posted it somewhere and some people believed it.
I know no programmer who followed such an idiotic idea.
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
Dumb programers misuse goto's.. therefor we take goto's out of the lang, and now we have smart programers wright ?
No.. You still have dumb programers,which will screw up in some other way.
it's a simple logical fancy.
Lots of macros to hide the scary gotos and make them look like regular try catch blocks?
Them gotos are evil!
Dim numsToClean As New List(Of Integer)
For indx = 1 To 3
If Not AquireResource(indx) Then
Cleanup(numsToClean)
Return
End If
numsToClean.Add(indx)
Next
DoStuffWithResources()
Cleanup(numsToClean)
Millions long for immortality who do not know what to do with themselves on a rainy Sunday afternoon. -- Susan Ertz
Amen
no
Deep nesting is not "cleaner."
Deep nesting and "goto"s are both symptoms of poor design, so you shouldn't be using either. Instead a complex function should be split up into logical segments that are implemented as separate subroutines, that each perform a clear and well defined task.
Don't know much then eh? http://en.wikipedia.org/wiki/R...
It's been a topic of debate for decades, right up there with the GOTO thing... Actually they are generally the SAME arguments either way, but hey.... Who am I?
"File to fit, pound to insert, paint to match" - Aircraft Maintenance 101
> Please explain how else to handle error conditions sanely in C? For example, how do you cleanly implement a function that needs to acquire N resources where any one of the acquisitions can fail and on failure you need to clean up after yourself?
If, say, a malloc() fails then it will return a null. In the clean up just test the pointer and only free if not null.
How hard is that ?
This compiles in my head (it's been a looooong time since I last touched C) and uses three allocations and several places that can fail.
Feel free to bash and/or offer constructive criticism. There's probably some official order to how things should be allocated but I don't know what it is. Maybe memory and sourcefile should have come first to minimize filesystem impact. First thought is that the loop in the middle could be moved to a function to move x bytes from file a to file b using buffer c which relies on x a b c correctness instead of testing it.
If I have been able to see further than others, it is because I bought a pair of binoculars.
It might be a "rule of thumb" but some of us aren't all thumbs. Returns in switch statements when no resource allocation has yet been done are clean and clear.
"Transparent" is a shit show that trades on every stereotype going. A man in drag is NOT a transsexual.
The key point is how many lines of resource allocation and clean up are required. If resource allocation and clean up are one line each then some nested if's are fine. But if the resource allocation and clean up are both huge chunks of code then breaking it out into separate functions is better.
It's so satisfying to see research like this. Every goto is this miniature jihad of people who are scared of it getting out their pikes, every piece of code that should use it making you wonder "am I gonna get raked over the coals? is this battle worth it?".
Hopefully the appropriate use of unconditional jump in mid and high level code will be appraised based on whether it's correct and good, not based on snap judgments and zealots.
Christmas in February IMO.
Research like this is very satisfying. Every correct use of goto is like "will I face jihad over this? Is it worth fighting this battle?".
Maybe in the future we'll see less anti-goto zealotry crapping on the correct uses of goto.
Christmas in February IMO.
Well, and who am I? :D
I don't care what idiots wrote into wikipedia.
Where I worked the last 35 years people return immediately with the result from the function, just like a mathematician would write it.
If you would adhere to a stupid 'only one return' rule you likely would be dragged into the basement and punished
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
The real question is, if someone writes the goto thing above, are you gonna scream at them until they triangle it?
If so, I think you are fighting a religious war for no reason.
Try 80 columns using the Linux kernel coding convention using 8-character tabs for indentation. The code grows unreadable quite fast.
This post is encrypted twice with ROT-13. Documenting or attempting to crack this encryption is illegal.
Try using C++ in the Linux kernel or in a bootloader, the type of stuff I work on. Everything is C for a reason. That isn't to say that C++ can't be used at the kernel level since I worked on a large C++ OS/2 driver (around 100KLOC) years ago, but it took a lot of work to make C++ work in that case and it was extremely compiler dependent (I think it only worked with Watcom 10.0b, not rev c or any other version).
This post is encrypted twice with ROT-13. Documenting or attempting to crack this encryption is illegal.
(Body added because Slashspot won't let me post a subject-only post, which has now ruined some of its effectiveness).
And that non-standard indentation will cause nothing but trouble, especially in a large project. The lack of proper indentation makes the code harder to read than just using a goto, especially if someone works on it in a modern editor that does things like handling indentation for you.
This post is encrypted twice with ROT-13. Documenting or attempting to crack this encryption is illegal.
Write an example that properly handles 5 different mallocs within the same function and properly cleans up after itself on failure without using goto's. Here's the goto example:
Resources *Resources_alloc(void)
{
Resources *ret;
if (NULL == (ret = malloc(sizeof(Resources))))
goto FAIL;
if (NULL == (ret->rsrc1 = malloc(sizeof(ret->rsrc1))))
goto FAIL_RESOURCES;
if (NULL == (ret->rsrc2 = malloc(sizeof(ret->rsrc2))))
goto FAIL_RSRC1;
if (NULL == (ret->rsrc3 = malloc(sizeof(ret->rsrc3))))
goto FAIL_RSRC2;
if (NULL == (ret->rsrc4 = malloc(sizeof(ret->rsrc4))))
goto FAIL_RSRC3;
assert(ret);
goto END;
/* error handling and return */
FAIL_RSRC3:
free(ret->rsrc3);
FAIL_RSRC2:
free(ret->rsrc2);
FAIL_RSRC1:
free(ret->rsrc1);
FAIL_RESOURCES:
free(ret);
ret = NULL;
FAIL:
assert(!ret);
END:
return ret;
}
Also, it isn't always the case that the thing you need to reclaim is a pointer and can be dynamically tested as to whether it was successfully acquired or not. For example, you can't test a pthread_mutex_t to see if it is valid or not. Your code has to know either based on its structure (i.e. - like the above) or you have to have a separate tracking variable per such object.
PS - Yes, the example is a bit contrived, there'd be little reason to malloc those members rather than just have them allocated as part of the Resources struct. I just did that as an illustrative example. Also, I'd typically cast the result of a malloc to the appropriate type so that a C++ compiler wouldn't complain about my code, but I haven't specified their types in this example.
yeah, GOTO the end and do some cleanup and/or error reporting, which is common to all the gotos. I don't see a problem with that. It's clear, simple and avoid heinous if/else deep nestings.
The problem is goto is not just used in the "heinous" nesting cases. And "heinous" varies with the length of the conditional blocks. If there are many levels but it fits on a screen or two, perhaps leans toward if. If they are stretching for many screens worth of code maybe lean towards goto.
The problem with goto is that the conditional code is at the same nesting level as the unconditional code so it makes it less apparent what code is conditional. Using goto should be the exception, to substitute a smaller problem for a larger problem, not the rule, substituting a small problem for no existing problem at all (minor nesting for short durations).
Returns in switch statements when no resource allocation has yet been done are clean and clear.
To quote myself from my previous post...
Still there are exceptions, but in general, one return, at the bottom, is best.
Yes, there ARE exceptions and I've done it too, usually for error cases where the function simply cannot continue, but in most cases, one return is all you need or want.
So what exactly have you DONE with your thumbs? On second thought, I don't want to know.
"File to fit, pound to insert, paint to match" - Aircraft Maintenance 101
Yea, well I learned to do structured program in PASCAL so I guess my roots are showing, well that and assembly where "return from subroutine" happened willy nilly at times, just like "Jump" (aka GOTO) and was a source of much agony for the careless...
Feel free to write as many returns and GOTOs into your functions as you want cause as one ole fart to another... I personally don't care HOW you wrote it as long as it runs and I don't have fix it for you. Unless you are working for me, in which case you might find yourself reading a pesky coding standard designed to scrape the rough edges off your habits so the next guy I hire can actually fix your mess after you are gone.
"File to fit, pound to insert, paint to match" - Aircraft Maintenance 101
I'm working on a chip with extremely limited memory. We run out long before the project is working well enough for even a demo, so we must start optimizing for space before it's correct or understandable.
If you've run out of memory before you've run out of steps in the algorithm, you first do the profiling to see how many bytes each method takes. Then you size-optimize the parts that are correct (per your unit tests) and leave the parts that aren't correct for later.
There are other cases I've had in the past where a real time deadline can't be met without doing some optimization (which generally means stop coding the naive way). [...] Not optimizing prematurely means doing a linear search through an array instead of using a built in function or data structure that is more efficient.
You bring up a good point. In the case of gross inefficiency causing your real-time program to miss deadlines, performance is part of correctness because missing deadlines is an integration test failure. After you do the profiling to see what's using so much time, you can put together unit tests that check inner loops for reasonable performance, and then you can optimize for both logical correctness and performance correctness.
void func() {
if (AquireResource1()) {
if (AcquireResource2()) {
if (AcquireResource3()) {
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Cleanup1();
}
}
With 4-spaces for code indentation and the usual 80chars long line limitation, the readability becomes ridiculous.
+1 for goto.
if ((not_too_long_a = a_bit_longer_resource_a()) != NULL) {
if ((not_too_long_b = a_bit_longer_resource_b()) != NULL) {
if ((not_too_long_c = a_bit_longer_resource_c()) != NULL) {
if ((not_too_long_d = a_bit_longer_resource_d()) != NULL) {
if ((not_too_long_e = a_bit_longer_resource_e()) != NULL) {
if (lets_try_to_add_some_logic_too == not_unusual) {
if (we_are_almost_out_of_space) {
oh_shit_we_are_running_out_of_space(we_have_a_variable_here, and_another_v
How about the contrived example of you actually specifying what acquireResources() should look like in C?
Notice that my example was NOT for error-handing, which is the usual excuse. GOTO is fine, same as JMP, JNE, JLE, etc.
Do you also think that one-line ternary expressions are bad (or chained ternary expressions)? Or that people should code it as if(1==x) instead of if(x==1) because they might forget to use the second equal sign? I "get" the logic behind both, but experience fixes those problems.
I have my style of coding, and if you ask 10 programmers what the ideal style is, you'll get back 11 (or more) opinions. It's no big deal :-)
"Transparent" is a shit show that trades on every stereotype going. A man in drag is NOT a transsexual.
Here's an alternate form that uses goto's:
{
if (-1 != (dest = creat("file.txt", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP))) {
perror("Unable to create file.txt");
goto FAIL;
}
if (-1 != (src = open("source.txt"))) {
perror("Unable to open source.txt");
goto FAIL_DEST;
}
if (NULL != (buf = calloc(1024, 1))) {
perror("Memory allocation error");
goto FAIL_SRC;
}
if (-1 != (rd = read(src, buf, 1024))) { /* should reading zero bytes be an error? */
perror("Read failed");
goto FAIL_BUF;
}
for (total_written = 0; total_written < rd; total_written += wr, buf_p += wr) { /* TODO check errno for fixable errors like EAGAIN or EINTR */
if (-1 == (wr = write(dest, buf_p, rd-total_written))) {
perror("Error writing to file");
goto FAIL_BUF;
}
}
FAIL_BUF:
free(buf);
FAIL_SRC:
close(src);
FAIL_DEST:
close(dest);
FAIL:
}
I prefer this form because the main line execution is significantly clearer (i.e. - you don't have ever deeper nesting of the successful path), especially if you have even more resources to acquire that can fail. Also, errors are handled immediately (e.g. - the associated perror() is right there where the error is detected rather than potentially hundreds of lines below) and the main line of execution is obviously aborted and cast off to error handling / cleanup code.
An even better example would be an initializer function that only releases the acquired resources on error and on success leaves them acquired when it returns. There, the if-else structure you used won't work -- or your successful return is buried deep inside your nested successful if's with a different exit point for errors.
C++ has FAR too many evil temptations in it to be allowed in many situations: the sword itself incites to violence.
C is far simpler and easy to understand what is happening.
Sure but that doesn't help people programming in C. You might as well tell them to program in Lisp because it's better.
Goto is useful for implementing constructs that are missing. I don't know if this has been resolved in modern C, but I do remember that older versions at least are missing a multi-level break statement.
break 3;
For example, should break out of loops 3 layers of control loops. Documented properly, this is one example of a goto. I also agree with other commentators that in a language where you can throw/raise an exception that gotos very likely don't have a use; you already have a pattern for breaking out of normal flow and jumping in to some error handling routine.
if (!AquireResource1()) return;
if (!AquireResource2()) { Cleanup1(); return }
if (!AquireResource3()) { Cleanup2(); Cleanup1(); return }
DoStuffWithResources();
}
it's about the same to me.
Your last place would have found greenbar printouts useful.
His ignorance covered the whole earth like a blanket, and there was hardly a hole in it anywhere. - Mark Twain
As I said in another post, I never had the need to use a goto since I don't program in Fortran (Cyber 205) nor Basic (Apple ][) anymore.
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
linux-3.16-rc5 # grep -r "goto" | wc -l
116607
From Documentation/CodingStyle:
Albeit deprecated by some people, the equivalent of the goto statement is
used frequently by compilers in form of the unconditional jump instruction.
The goto statement comes in handy when a function exits from multiple
locations and some common work such as cleanup has to be done. If there is no
cleanup needed then just return directly.
The rationale is:
- unconditional statements are easier to understand and follow ;)
- nesting is reduced
- errors by not updating individual exit points when making
modifications are prevented
- saves the compiler work to optimize redundant code away
example:
if (id < 0) {
id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
err = id;
goto exit;
}
}
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
if (rtc == NULL) {
err = -ENOMEM;
goto exit_ida;
}
[.......]
exit_kfree:
kfree(rtc);
exit_ida:
ida_simple_remove(&rtc_ida, id);
exit:
dev_err(dev, "rtc core: unable to register %s, err = %d\n",
name, err);
return ERR_PTR(err);
I have written a hell of a lot of c and c++ in the last quarter of a century and have never used goto. I have used setjmp/longjmp though.
The problem with exceptions is that you have to hold distant code (the exception handler) in your mind along with the code causing the exception.
The reason why GOTO was considered bad code was because you had to hold a map of where the code gone to was and what the code was doing before it went there.
I.e. the same damn problem.
ALL programming necessities are so that you have code that can be understood easily. Extending it requires understanding. Refactoring (which will be necessary after sufficient numbers of extension for any code) requires understanding.
I've never understood the "deep nesting" thing. Just do this:
bool success = doSomethingThatMightFail(); ....
if (success) {
success = doSomethingElseThatMightFail();
}
if (success) {
success = doMoreFailingStuff();
}
if (success) {
success = yepMoreFailingStuff();
}
if (!success) {
cleanupWork
}
If You don't think it's possible without independent wealth to walk away from a job then you're admitting our capitalist system is working off slave labour.
Well done, sheep.
"GOTO" should never have been allowed in C.
That is a silly statement. There are always someone able to abuse even the safest constructs, so removing any "unsafe" construct is not the solution. Teaching how not to use them, is however, a much better strategy.
Maybe programmers prefer to read code in a linear fashion, rather than a Choose-Your-Own-Adventure book.
In my last job I was dealing with a lot of extremely complicated Excel and Access macros written by finance majors. Smart people, but people who had zero formal programming experience. I opened up a module one day and this is what I saw:
if foo1 then [VBA requires a "then"]
bar1
Goto Label1
end if
bat1
Label1:
if foo2 then
bar2
Goto Label2
end if
bat2
Label2:
I stared at my screen for probably ten whole seconds and then began laughing uncontrollably, then kept laughing as I alternated shaking my head and occasionally shrugging in halfhearted approval. The guy invented an else. He didn't know about the existing else so he found a simple "design pattern" that did what he needed and used it. Once you got over the initial ridiculousness, it was pretty easy to follow.
And that *is* all that an "if" is: a species of goto. So are continuations. So are a lot of other control structures. This sentiment--"you shouldn't use a Goto when it is simple and intuitive; instead, you should use a bunch of conditional Gotos in an unintuitive way"--is completely incomprehensible to me. It's not merely saying 'never taking off the training wheels; it's saying, "if the training wheels make a certain turn in the path too difficult, just get off the bike, pick it up, gently turn it where it needs to go, and hop back on." If large projects with code monkeys require tons of least-common-denominator idiotproofing, so be it. But competent experts are not wrong to say that sometimes you need to take off the training wheels. I've yet to hear a coherent argument about the wrongness of Goto that doesn't boil down to "this is what I was always taught", "I'm not imaginative enough to think of a use case", or "you have to prevent stupid programming from doing stupid things."
In other words, you are just an arrogant bastard with no good answer.
Sure.
Political correctness is really just herd psychology pushed by insecure people who desperately seek social conformity.
Sheesh... kids... The proscription of GOTO was a general principle. Some languages did not adequately accommodate single-entrance-single-exit. Therefore, branching to an exit statement was never considered a violation of structured programming. Of course, if the language in question DOES have the appropriate construct -- as does C and its variants -- then the use of GOTO would be frowned upon. As one of my instructors, back in the day, used to say, "If you have to GOTO, then ask yourself why you aren't there already."
Not different, but refined:
#define TRY(_cond,_status,_exitpoint,_msg...) \
do { if (_cond) { \
status=(_status); \
printf("Failed whlie " _msg); \
goto _exitpoint; \
} } while(0)
void func() {
int status=0;
TRY(!AquireResource1(), -1, end, "acquiring resource 1");
TRY(!AquireResource2(), -1, cleanup1, "acquiring resource 2");
TRY(!AquireResource3(), -1, cleanup2, "acquiring resource 3");
DoStuffWithResources();
TRY(!Cleanup3(), -2, panic, "releasing resource 3");
cleanup2:
TRY(!Cleanup2(), -2, panic, "releasing resource 2");
cleanup1:
TRY(!Cleanup1(), -2, panic, "releasing resource 1");
end:
panic:
return status;
}
Advantages:
* Boils code down to its essence ("if" and "goto" are actually *boilerplate* when you follow the idiom.)
* Lends itself to consistent use
* Actually *useful* (as in, "not merely cosmetic") code documentation
* Every "important" is wrapped with a macro(!!!); If you apply this idiom consistently, you can instrument the wazoo out of your code, post-implementation, with things like tracing, backtracing, and all kinds of crazy stuff like single-stepping (use a semaphore) or whatever you can imagine, all throughout your code, by simply changing the definition of the TRY macro.
Comment removed based on user account deletion
I think "designed" was my word error. GOTO was invented to make sense to the processor, but it's use combined with functions ended its time as the best idea to use.
Oh?
#define skipto goto ...
skipto cleanup;
That makes you happy then?
Do you seriously think this is cleaner? Especially when in practice the resource-acquisition functions can be called in different order (or not at all) on different code paths depending on what gets returned by other functions?
How 'bout (or were functions ruled out?):
void func() {
if (AquireResource1())
func2();
}
void func2() {
if (AquireResource2())
func3();
Cleanup1();
}
void func3() {
if (AquireResource3()) {
DoStuffWithResources();
Cleanup3();
}
Cleanup2();
}
Ecstatic.
Political correctness is really just herd psychology pushed by insecure people who desperately seek social conformity.
By and large goto is OK when used as an exit path from a nested context (as others here have shown in examples). Some languages provide explicit constructs for this (like variants or exit and continue that allow one to specify which nested context is being exited). C doesn't have these; so, one has to make do.
BTW, if you want to know what's bad about gotos, track down some Fortran programs that were written in the 60s and you will see where Dijkstra was coming from :-)
An engineer who ran for Congress. http://herbrobinson.us
If you like your GOTO you can KEEP your GOTO