Linux Coding Style Guide: GOTOs not always considered harmful.
kernel.org
kernel.org
The first is the lack of exceptions. This is not necessarily a deficiency in kernel code because it can make other code more complex, but fundamentally error handling and cleanup usually either requires 1) keeping track of a bunch of state and then using complicated logic to evaluate the state for error cases 2) exception catching such that error conditions can be thrown up, caught, and brought to a cohesive resolution or 3) goto, such that cohesive cleanup code can be placed together in a segment and then jumped to on error conditions. (3) is far superior to (1) and, while (2) is arguably superior to (3), it simply doesn't exist in C.
The second case is that C doesn't have a multi-level break. That is, when in multiple nested blocks (loops), in order to exit early one would have to keep track of state and use conditionals to trigger a "waterfall" break all the way down the block chain. goto solves this by allowing you to jump out of the chain completely. I think most people would probably agree that adding a multilevel break (e.g., "break 2;") would probably be preferable to using goto here, but I think the workaround is basic enough (either use goto or refactor into separate functions) that the language addition is unlikely now.
switch( machineState ){
case ALPHA:
...
break;
case BETA:
...
goto location;
case GAMMA:
location:
...
}
No, I wasn't familiar with Duff's device until just now. I'd say that you only need to be explicit about pointing out fallthroughs if the blocks in the switch statements are more than a couple of lines long. switch( machineState ){
case ALPHA:
...
break;
case BETA:
...
/* fallthrough */
case GAMMA:
...
} switch ( machineState ) {
case ALPHA:
... alpha prep ...
goto gamma
case BETA:
... beta prep ...
goto gamma
case GAMMA:
gamma:
...
}
? (EDIT: Not to say that you shouldn't still comment, of course.)It also might be clearer to move GAMMA outside the switch block entirely:
int shouldGamma = 0;
switch ( machineState ) {
case ALPHA:
... alpha prep ...
shouldGamma = 1;
break;
case BETA:
... beta prep ...
shouldGamma = 1;
break;
case GAMMA:
shouldGamma = 1;
break;
}
if(shouldGamma) {
// GAMMA stuff here
}
The shouldGamma flag would be unnecessary if all possible values for machineState should still run the code in GAMMA.That being said, Richard Stevens, a respected author on Unix programming, had this to say:
http://www.kohala.com/start/rstevensfaq.html Q: Why do your programs contain gotos?
Read Structured Programming with go to Statements by Knuth in the ACM Computing Surveys, Vol. 6, No. 4, Dec. 1974 issue. (In fact, this entire issue of Computing Surveys is a classic.) My challenge to the goto-less programmer is to recode tcp_input() (Chapters 27 and 28 of TCP/IPIv2) without any gotos ... without any loss of efficiency (there has to be a catch).
Then an expert, highly-regarded dev shows code that has useful goto statements. And only 1% of coders understand that this is perfectly acceptable. They know that goto is not evil (all the time) and is even very useful in certain cases.
At that level it's all goto so you have to use them. And if you don't want your program to become a spaghetti nightmare you have to use them wisely.
When using anything higher level you can & should use 'if', 'for', 'while', etc. but it's not like the world implodes if you use a goto in your code.
Many conditional statements and sentinel null values can be replaced with strategy objects or mock objects, revealing a more functional programming style that can make testing easier.
At first I was more than a little bit turned off by the practice, but once I understood _how_ it was being used, it made a lot of sense. The bottom line is, goto statements which jump backwards are generally Considered Harmful. Goto statements which jump forwards, within a limited scope, and used conservatively, can serve to improve the readability of code.
I still almost never use them, but now I know when it would be appropriate to do so, rather than dogmatically rejecting the entire practice. I think the idea that gotos are always bad is one of those things that programmers tend to accept as "received wisdom", without considering the actual motivation for the principle, or in which cases it might not apply as strongly.
It's also more maintainable, since the error checking is done in a single place and a single jump. Without a goto, if there were a new error condition to check, it might take several updates to ensure all error conditions are accurately checked.
Generally, gotos for handling exceptional cases inside a function: okay. Goto for implementing twisted logic: bad.
I think goto's are fine, and they are often more readable than large amounts of indentation. That's _especially_ true if the code flow isn't actually naturally indented (in this case it is, so I don't think using goto is in any way _clearer_ than not, but in general goto's can be quite good for readability).
Of course, in stupid languages like Pascal, where labels cannot be descriptive, goto's can be bad. But that's not the fault of the goto, that's the braindamage of the language designer.