When I learned C, teacher told me all day long: "Don't use goto, that's a bad habit, it's ugly, it's dangerous!" and so on.
Why then, do some kernel programmers use goto, for example in this function, where it could be replaced with a simple
while(condition) {}
or
do {} while(condition);
I can't understand that. Is it better in some cases to use goto instead of while/do-while? And if so, why?
Historical context: We should remember that Dijkstra wrote Goto Considered Harmful in 1968, when a lot of programmers used goto as a replacement for structured programming (if, while, for, etc.).
It's 44 years later, and it's rare to find this use of goto in the wild. Structured programming has already won, long ago.
Case analysis:
The example code looks like this:
SETUP...
again:
COMPUTE SOME VALUES...
if (cmpxchg64(ptr, old_val, val) != old_val)
goto again;
The structured version looks like this:
SETUP...
do {
COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);
When I look at the structured version, I immediately think, "it's a loop". When I look at the goto version, I think of it as a straight line with a "try again" case at the end.
The goto version has both SETUP and COMPUTE SOME VALUES on the same column, which emphasizes that most of the time, control flow passes through both. The structured version puts SETUP and COMPUTE SOME VALUES on different columns, which emphasizes that control may pass through them differently.
The question here is what kind of emphasis do you want to put in the code? You can compare this with goto for error handling:
Structured version:
if (do_something() != ERR) {
if (do_something2() != ERR) {
if (do_something3() != ERR) {
if (do_something4() != ERR) {
...
Goto version:
if (do_something() == ERR) // Straight line
goto error; // |
if (do_something2() == ERR) // |
goto error; // |
if (do_something3() == ERR) // |
goto error; // V
if (do_something4() == ERR) // emphasizes normal control flow
goto error;
The code generated is basically the same, so we can think of it as a typographical concern, like indentation.
In the case of this example, I suspect it was about retrofitting SMP support into code that was originally written in a non-SMP-safe way. Adding a goto again; path is a lot simpler and less invasive than restructuring the function.
I can't say I like this style much, but I also think it's misguided to avoid goto for ideological reasons. One special case of goto usage (different from this example) is where goto is only used to move forward in a function, never backwards. This class of usages never results in loop constructs arising out of goto, and it's almost always the simplest, clearest way to implement the needed behavior (which is usually cleaning up and returning on error).
Very good question, and I think only the author(s) can provide a definitive answer. I'll add my bit of speculation by saying that it could have started with using it for error handling, as explained by #Izkata and the gates were then open to using it for basic loops as well.
The error handling usage is a legitimate one in systems programming, in my opinion. A function gradually allocates memory as it progresses, and if an error is encountered, it will goto the appropriate label to free the resources in reverse order from that point.
So, if the error occurs after the first allocation it will jump to the last error label, to free only one resource. Likewise, if the error occurs after the last allocation, it will jump to the first error label and run from there, freeing all the resources until the end of the function. Such pattern for error handling still needs to be used carefully, especially when modifying code, valgrind and unit tests are highly recommended. But it is arguably more readable and maintainable than the alternative approaches.
One golden rule of using goto is to avoid the so-called spaghetti code. Try drawing lines between each goto statement and its respective label. If you have lines crossing, well, you've crossed a line :). Such usage of goto is very hard to read and a common source of hard to track bugs, as they would be found in languages like BASIC that relied on it for flow control.
If you do just one simple loop you won't get crossing lines, so it's still readable and maintainable, becoming largely a matter of style. That said, since they can just as easily be done with the language provided loop keywords, as you've indicated in your question, my recommendation would still be to avoid using goto for loops, simply because the for, do/while or while constructs are more elegant by design.
Related
I've recently been taking a C course as part of a university degree. We have been forbidden from using goto, break and continue, and must only use a single return statement from a function.
I have been writing C for many years as a hobby without these restrictions and have been struggling to write in the style they require - often ending up with mountains of nested if statements. I try to avoid goto where possible, but have a habit of using break, continue and multiple return statements very liberally and have never had any issues.
Why are these features considered bad practice?
This is highly subjective, as all 4 of those things have trade-offs--pros and cons.
Ask your teacher, and report back here.
goto has an unnecessarily bad reputation. People fear it waaay more than they should! I think it is because using goto can be easily abused, especially if you jump outside your function, or jump backwards (upwards in the code). If you jump only downwards, however, and within the same function, not crossing over variable declarations in the process, goto can be an excellent technique to help you achieve a clean single return statement at the end of a function. In C, it can be the perfect way to handle errors in complex initializations! See my usage in my answer here: Opaque C structs: various ways to declare them. See also the Additional reading and justification for valid usage of goto in error handling for professional code section at the bottom of my answer there.
In some organizations, including some safety-critical embedded microcontroller applications I have worked in in the self-driving car industry, using goto can even be required. I've worked in code bases where the above error handling technique with a single return and using goto was required by my organization's coding guidelines. It's not automatically evil. Rather, it's a tradeoff with pros and cons. Unfortunately, many people I've worked with also have an unfounded revulsion towards goto which was probably planted by their teacher, like your teacher for instance. This revulsion can lead to some pretty awful code at times when goto would have beautified and simplified it tremendously.
Note also that in C++ there are other ways to do error handling that aren't as accessible in C, but still, goto could be used.
break and continue are much less-easily abused. Perhaps the only real argument against them is code clarity.
Ex: do something 10 times:
// fine, but perhaps not as clear
const int NUM_TIMES = 10;
int i = 0;
while (true)
{
printf("i = %i\n", i);
i++;
if (i >= NUM_TIMES)
{
break;
}
}
// clearer (withOUT `break`)
int i = 0;
while (i < NUM_TIMES)
{
printf("i = %i\n", i);
i++
}
// or
for (int i = 0; i < NUM_TIMES; i++)
{
printf("i = %i\n", i);
}
// The above example is trivial, but imagine you are NOT just
// incrementing a number! The non-`break` while loop with your
// condition up-front can be clearer. The `break` option is just
// fine too, but it's a matter of taste.
A single return statement is best achieved when using goto. Otherwise, it requires tons of nesting. So, that is contrary to your teacher's guidance, it seems.
Many people, however, opt for multiple return statements to exit a function early and avoid tons of nested braces. It's basically a tradeoff of:
tons of nesting, OR
goto, and a single return, OR
multiple returns
In languages like C, I use goto and a single return, unless my stinking peers won't approve it, and they fight with me, in which case I conform to whoever is reviewing me so I can get my stinking code merged. :) (Replace "stinking" with "beloved", for your peers, of course).
In languages like C++, where developers hate goto even more than in C, I generally use multiple returns or extra nesting and breaking the code out into sub-functions--again, to appease my peers who don't know and love C.
In Python, where goto does not exist, I use multiple returns.
At the end of the day, ask your teacher. Take mental notes. Learn why they are doing it. Learn from us. Learn from books. Form your own opinions. Begin recognizing trends and "best practices", and form your own opinions. If you need to do what your teacher wants, even if they're wrong, for the grade, do it. But do it the "better" way you determine outside that class.
In code reviews, if your stinking peer won't approve your beautiful goto usage, try to convince them. If you can't, make the change to avoid goto, reduce friction, and move on. It's not worth fighting about. Everything is a tradeoff. There are many "right" ways to do things.
Note to all: I say "stinking" in jest. I am very grateful for the job that I have.
TODO (Notes to self):
Try forcing goto to jump outside the function, even if it produces undefined behavior, as a demo to prove it is dumb:
goto jump outside function
https://stackoverflow.com/a/44843514/4561887
The goto statement has been examined at great length in several SO discussions (see this and that), and I certainly don't want to revive those heated debates.
Instead, I'd like to concentrate on a single use case of gotos and discuss its value and possible alternatives.
Consider the following code snippet, which is common in (at least my own) FSMs:
while (state = next_state()) {
switch (state) {
case foo:
/* handle foo, and finally: */
if (error) goto cleanup;
break;
case bar:
/* handle bar, and finally: */
if (error) goto cleanup;
break;
/* ...other cases... */
}
}
return ok;
cleanup:
/* do some cleanup, i.e. free() local heap requests, adjust global state, and then: */
return error;
Swapping out the cleanup stuff in a separate function just in order to save the gotos seems awkward. On the other hand, we've been raised to condemn the use of gotos wherever possible.
My question: is my code example considered good style?
If not, are there feasible alternatives available?
Please adhere to the specific usage of goto described above. I don't want to delve into yet another discussion about the general use of goto.
Your usage of goto is ok. It doesn't break the 2 good ways to use goto.
gotos MUST go down (a few lines) in the source
The innermost block of goto labels MUST contain the goto statements
Instead of extracting the cleanup logic into its own function and calling that from different places, I would consider extracting the switch statement into a separate function and returning an error code from that. In your while loop you could the check the return code and do the cleanup and return if neccessary.
If you have several resources shared between the switch and cleanup logic then I think the goto would be preferrable to passing all this state around.
I have seen goto used in this manner in the OpenBSD kernel, particulary within ATA device drivers (one such example) and I personally feel that it is good style, as it helps illustrate exactly what is happening and how the code matches up to the corresponding FSM. When trying to verify functionality of an FSM against a specification, this use of goto improves clarity somewhat.
Goto is not needed when you have switch. Using both switch and goto just adds complication.
while (state) {
switch (state) {
case cleanup:
/* do some cleanup, i.e. free() local heap requests, adjust global state, and then: */
return error;
case foo:
/* handle foo, and finally: */
if (error) { state = cleanup; continue; }
break;
case bar:
/* handle bar, and finally: */
if (error) { state = cleanup; continue; }
break;
/* ...other cases... */
}
state = next_state();
}
return ok;
I'd say if the cleanup code can't be generalized, i.e. it's specific for the function it's being used in, the goto is a nice and clean way to do it.
Looking at Ben Voigt's answer gave me an alternative answer:
while (state = next_state()) {
switch (state) {
case foo:
/* handle foo, and finally: */
/* error is set but not bothered with here */
break;
case bar:
/* handle bar, and finally: */
/* error is set but not bothered with here */
break;
/* ...other cases... */
}
if (error) {
/* do some cleanup, i.e. free() local heap requests, */
/* adjust global state, and then: */
return error;
}
}
return ok;
The downside of this is that you have to remember, after it process a state, it might clean up if there's an error. It looks like the if structure could be an if-else chain to handle different types of errors.
I haven't taken a formal class on FSMs, but it looks to me like the code you posted has the same behavior.
If all your init code is done before entering the while loop, then your gotos are useless, you can do the cleanup when exiting the loop. If your state machine is all about bringing up stuff in the correct order, then why not, but since you have a state machine, why not use it to do the cleanups ?
I am not against goto when initializing several thing together, and having a simple error handling code, as discussed here. But if you go through the trouble of setting up a state machine, then I can't see a good reason to use them. IMO, the question is still too general, a more practical example of state machine would be useful.
A general principle I like to follow is that one should when possible try to write code whose flow and design fits that of the problem domain ("what the program needs to do"). Programming languages include control structures and other features which are good fits for most, but not all, problem domains. Such structures should be used when they match the program's requirements. In cases where a program's requirements are not a good match for a language's features, I prefer to focus on writing code which expresses what the program needs to do, than to contort the code to fit patterns which, while they meet the requirements of other applications, don't really meet the requirements for the program being written.
In some cases, a very natural way of translating a state machines into code, in cases where a routine will be able to not have to "exit" until a state machine has run to some form of conclusion, is to have a goto label represent each state, and use use if and goto statements for the state transitions. If the required state transitions would be a better fit for other control structures (e.g. while loops), using such loops would be better than goto statements, and using switch statements may make certain kinds of "adaptations" (e.g. having a routine perform state transition each time it's executed, rather than requiring it to immediately run to completion) much easier. On the other hand, since a switch statement is really just a "goto" in disguise, it may be cleaner to simply use a goto directly than use a switch statement to imitate one.
The use of goto to cleanup the code by breaking multiple nesting for loop is very convenient, instead of setting flags and checking it in each nesting. For example, if you are unable to open a file and you discover it deep in a nesting, you can simply goto the cleanup segment and close the files and free resources. You can see such goto examples in the coreutilities tools sources.
If you just need some cleanup code to be able to be called from multiple places in your procedure and it needs to access local resources, maybe use a statement lambda instead. Define it before your switch logic and just call it where you need to clean up.
I like the idea for a couple reasons:
1) It's cooler than a goto (and this is always important)
2) You get the clean encapsulation of logic without having to create an external method and pass a bunch of parameters since the lambda can access the same local variables withing the closure.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Should a function have only one return statement?
Hello,
gcc 4.4.4 c89
Is it good programming practice to return from 1 point in a function.
I have written a function below. However, I am returning from 2 possible points.
Is this good style?
static int init_data(struct timeout_data_t *timeout_data)
{
if(timeout_data == NULL) {
fprintf(stderr, " [ %s ] [ %d ]\n",
__func__, __LINE__);
return FALSE;
}
/* Assign data */
timeout_data->seconds = 3;
timeout_data->func_ptr = timeout_cb;
return TRUE;
}
If it aids readability, then there is nothing wrong with it.
Personally, I write this kind of code all of the time.
This is an ongoing religious-style debate without an accepted answer. There are many people on both sides of the argument, who feel strongly about it.
I don't think there's anything wrong with it personally, but the best approach is to go with the style guidelines of your team, if they have some (and if not, just ask about it. If anyone recoils in horror, it would be kinder to stick to single-return-point).
I've had managers that lived and died by the 1 return policy for the sake of "readability", even though it's much more readable in some cases without it.
The bottom line is... if the man that signs your paycheck says you're only going to use 1 return, use 1 return. The best way to do this is
type myfunc(params) {
type result = defaultValue;
// actual function here, word for word
// replace "return $1" with "result = $1"
return result;
}
This is a valid way to do things in their book, and will smile at your 1 return policy adherence. Of course, you know using this adds ZERO readability because all you've done is replace "return" (which is syntax highlighted) with "result =" which is not. But you've made your boss happy, which when you break it all down is what development is about anyway, right? :-)
In straight C, I think that error checking/parameter verification at the top of the function with a return (or possibly even multiple return points in the parameter verification) results in reasonably clean code. After that point, though, my opinion is that it is a good idea to have one single return at the bottom of the function. That helps avoid problems with cleanup (e.g., freeing of memory) that might be allocated in the workings of the function.
There's nothing inherently wrong about having more than one exit point, especially when you're returning on errors. Returning immediately usually makes for clearer code than having the whole thing wrapped in an if/else statement and setting some result flag to be returned at the end. (When you see "return result;", you have to look through all of the earlier code to see how and when result gets set. More moving parts == less clarity.)
You've tagged your questions as "C" which makes a difference.
In C you might write code such as
open file
process data
close file
If you put a return in the middle of the process data section then you're likely to skip the essential cleanup so it might be considered bad practice to have multiple return points because it's very easy to mess up.
If it was C++ then its best practice to let destructors handle cleanup so it's not nearly such a potential problem so this advice is somewhat obsolete in c++
As Oded and Andrzej Doyle pointed out there is nothing wrong with it.
They is no such thing as a golden rule when it comes to this.
The first an most important thing you have to keep in mind when writing code is that some one else will have to read it and make sense out of it. Maybe you will have to go about it in a couple of months, and if you have made a mess you will regret it.
Personally I always:
if the code is new used the coding style everybody else is using in the project.
If editing others code used the coding style already implemented there.
Avoid above all code optimizations (the compiler is best at that).
keep it clean and lean.
If your function is small enough (10-15 lines), as it should be :), then it really doesn't matter if you use a single return point or multiple one. Both are equally readable.
Problems start cropping up with badly designed large functions. In such cases both the styles, returning from a single point, and returning from multiple points, further complicates the function, although even in such cases I prefer returning early and returning at multiple points.
It's often the case that you have to check for several conditions etc before you start with the real work, and then you are tempted to do an early return, as in your code. I think this is fine for short methods, but when it gets more complicated I'd suggest to break your code in a "setup and check" method and a "real work" method, both having only one exit. Of course as long as it's readeable, it's fine to have multiple returns (e.g. in a long switch statement).
Failing (and thus returning) early is a very very very good practice. All the code after the checks is free of a lot of potential errors.
Generally, it is good practice to avoid GOTOs. Keeping that in mind I've been having a debate with a coworker over this topic.
Consider the following code:
Line:
while( <> ) {
next Line if (insert logic);
}
Does using a loop label count as a goto?
Here is what perlsyn in perldoc has to say:
Here's how a C programmer might code up a particular algorithm in Perl:
for (my $i = 0; $i < #ary1; $i++) {
for (my $j = 0; $j < #ary2; $j++) {
if ($ary1[$i] > $ary2[$j]) {
last; # can't go to outer :-(
}
$ary1[$i] += $ary2[$j];
}
# this is where that last takes me
}
Whereas here's how a Perl programmer more comfortable with the idiom might do it:
OUTER: for my $wid (#ary1) {
INNER: for my $jet (#ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
My take on this is no because you are explicitly telling a loop to short circuit and advance however my coworker disagrees, says that it is just a fancy GOTO and should be avoided. I'm looking for either a compelling argument or documentation that explains why this is or is not a GOTO. I'll also accept an explanation for why this is or is not considered good practice in perl.
Dijkstras intent was never that anything resembling goto is to be considered harmful. It was that the structure of code where gotos are used as the main construct for almost any kind of program flow change will result in what we today call spaghetti code.
You should read the original article and keep in mind that it was written in 1968 when labeled jumps was the main flow control constructs in just about all programming languages.
https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF
The danger of GOTO labels is that they create spaghetti code and make the logic unreadable. Neither of those will happen in this case. There is a lot of validity in using GOTO statements, much of the defense coming from Donald Knuth [article].
Delving into the differences between your C and Perl example... If you consider what is happening at the assembly level with your C programs, it all compiles down to GOTOs anyway. And if you've done any MIPS or other assembly programming, then you've seen that most of those languages don't have any looping constructs, only conditional and unconditional branches.
In the end it comes down to readability and understandability. Both of which are helped an enormous amount by being consistent. If your company has a style guide, follow that, otherwise following the perl style guide sounds like a good idea to me. That way when other perl developers join your team in the future, they'll be able to hit the ground running and be comfortable with your code base.
Who cares whether it counts as goto as long as it makes the code easier to understand? Using goto can often be MORE readable than having a bunch of extra tests in if() and loop conditions.
IMO, your code comparison is unfair. The goal is readable code.
To be fair, you should compare an idiomatic Perl nested loop with labels against one without them. The C style for and blocked if statement add noise that make it impossible to compare the approaches.
Labels:
OUTER: for my $wid (#ary1) {
INNER: for my $jet (#ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
Without labels:
for my $wid (#ary1) {
for my $jet (#ary2) {
last if $wid > $jet;
$wid += $jet;
}
}
I prefer the labeled version because it is explicit about the effect of the condition $wid > $jet. Without labels you need to remember that last operates on the inner loop and that when the inner loop is done, we move to the next item in the outer loop. This isn't exactly rocket-science, but it is real, demonstrable, cognitive overhead. Used correctly, labels make the code more readable.
Update:
stocherilac asked what happens if you have code after the nested loop. It depends on whether you want to skip it based on the inner conditional or always execute it.
If you want to skip the code in the outer loop, the labeled code works as desired.
If you want to be sure it is executed every time, you can use a continue block.
OUTER: for my $wid (#ary1) {
INNER: for my $jet (#ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
continue {
# This code will execute when next OUTER is called.
}
I think the distinction is somewhat fuzzy, but here's what the goto perldoc states about the (frowned upon) goto statement:
The goto-LABEL form finds the statement labeled with LABEL and resumes execution there.
...
The author of Perl has never felt the need to use this form of goto (in Perl, that is; C is another matter). (The difference is that C does not offer named loops combined with loop control. Perl does, and this replaces most structured uses of goto in other languages.)
The perlsyn perldoc, however, says this:
The while statement executes the block as long as the expression is true. The until statement executes the block as long as the expression is false. The LABEL is optional, and if present, consists of an identifier followed by a colon. The LABEL identifies the loop for the loop control statements next, last, and redo. If the LABEL is omitted, the loop control statement refers to the innermost enclosing loop. This may include dynamically looking back your call-stack at run time to find the LABEL. Such desperate behavior triggers a warning if you use the use warnings pragma or the -w flag.
The desperate behaviour bit doesn't look too good to me, but I may be misinterpreting its meaning.
The Learning Perl book (5th edition, page 162) has this to say:
When you need to work with a loop block that's not the innermost one, use a label.
...
Notice that the label names the entire block; it's not marking a target point in the code. [This isn't goto after all.]
Does that help clear things up? Probably not... :-)
Labeled loop jumps in Perl are GOTOs as much as C's break and continue are.
I would answer it like this, and I'm not sure if this is sufficiently different from what others have said:
Because you can only only move inside of the current scope, or to a parent scope, they're much less dangerous than what is typically implied by goto, observe:
if (1) {
goto BAR;
die 'bar'
}
BAR:
This should work obviously, but this won't (can't move in this direction).
if (0) {
BAR:
die 'bar'
}
goto BAR;
Many use cases of labels differ from goto in that they're just more explicit variants of core flow control. To make a statement that they're categorically worse would be to imply that:
LOOP: while (1) {
next LOOP if /foo;
}
is somehow worse than
while (1) {
next if /foo/;
}
which is simply illogical if you exclude style. But, speaking of style, the latter variant is much easier to read - and it does stop you from having to look up for the properly named label. The reader knows more with next (that you're restarting the loop in the current scope), and that is better.
Let's look at another example
while (1) {
while (1) {
last;
}
stuff;
}
-vs-
FOO: while (1) {
BAR: while (1) {
next FOO;
}
stuff;
}
In the latter example here next FOO, skips stuff -- you might desire this, but it is bad idea. It implies that the programmer has read a parent scope to completion which is an assumption probably better avoided. In summary, label isn't as bad as goto and sometimes they can simplify code; but, in most cases they should be avoided. I usually rewrite loops without labels when I encounter them on CPAN.
gotos are bad because they create hard to understand code--particularly, what is often called "Spaghetti Code". What's hard to understand about next Line...??
You can call it a loop "name", and it really is something to help emphasize loop boundaries. You're not jumping into an arbitrary point in relation to the loop; you're going back to the top of a loop.
Sadly enough, if it is a group or house standard, there might be nothing to convince the group that it's not a goto. I had a manager who absolutely insisted that a ternary operator made things hard to read, and preferred I use if-blocks for everything. I had a pretty good argument anything can be done in the clauses of an if-else, but that a ternary made it explicit that you were looking for a particular value. No sale.
This kind of jump is a disciplined used of a goto-like statement. So it's certainly less harmful than undisciplined use of goto. (As kasperjj wrote, "Dijkstras intent was never that anything resembling goto is to be considered harmful.")
IMO, this Perl kind of jump is even better design than C's "break" and "continue", because it makes clear what loop we break or continue, and it makes it more solid in the face of code changes. (Besides, it also allows to break or continue an outer loop.)
There are pundits who don't like break/continue and the like, but at some point there is a tradeoff to make between rules of thumb and readability, and a well-chosen break/continue or even goto may become more readable than "politically correct" code.
break/last and continue/next ARE gotos. I don't understand why anyone would go to such lengths to avoid a keyword yet use a different keyword that does the same thing...
4.4.4. Loop Control
We mentioned that you can put a LABEL on a loop to give it a name. The loop's LABEL identifies the loop for the loop-control operators next, last, and redo. The LABEL names the loop as a whole, not just the top of the loop. Hence, a loop-control operator referring to the loop doesn't actually "go to" the loop label itself. As far as the computer is concerned, the label could just as easily have been placed at the end of the loop. But people like things labeled at the top, for some reason.
Programming Perl
What advantage, if any, is provided by formatting C code as follows:
while(lock_file(lockdir)==0)
{
count++;
if(count==20)
{
fprintf(stderr,"Can't lock dir %s\n",lockdir);
exit(1);
}
sleep(3);
}
if(rmdir(serverdir)!=0)
{
switch(errno)
{
case EEXIST:
fprintf(stderr,"Server dir %s not empty\n",serverdir);
break;
default:
fprintf(stderr,"Can't delete dir %s\n",serverdir);
}
exit(1);
}
unlock_file(lockdir);
versus something more typical such as
while(lock_file(lockdir)==0) {
count++;
if(count==20) {
fprintf(stderr,"Can't lock dir %s\n",lockdir);
exit(1);
}
sleep(3);
}
if(rmdir(serverdir)!=0) {
switch(errno) {
case EEXIST:
fprintf(stderr,"Server dir %s not empty\n",serverdir);
break;
default:
fprintf(stderr,"Can't delete dir %s\n",serverdir);
}
exit(1);
}
unlock_file(lockdir);
I just find the top version difficult to read and to get the indenting level correct for statements outside of a long block, especially for longs blocks containing several nested blocks.
Only advantage I can see is just to be different and leave your fingerprints on code that you've written.
I notice vim formatting would have to be hand-rolled to handle the top case.
The top example is know as "Whitesmiths style". Wikipedia's entry on Indent Styles explains several styles along with their advantages and disadvantages.
The indentation you're seeing is Whitesmiths style. It's described in the first edition of Code Complete as "begin-end Block Boundaries". The basic argument for this style is that in languages like C (and Pascal) an if governs either a single statement or a block. Thus the whole block, not just its contents should be shown subordinate to the if-statement by being indented consistently.
XXXXXXXXXXXXXXX if (test)
XXXXXXXXXXXX one_thing();
XXXXXXXXXXXXXXX if (test)
X {
XXXXX one_thing();
XXXXX another_thing();
X }
Back when I first read this book (in the 90s) I found the argument for "begin-end Block Boundaries" to be convincing, though I didn't like it much when I put it into practice (in Pascal). I like it even less in C and find it confusing to read. I end up using what Steve McConnel calls "Emulating Pure Blocks" (Sun's Java Style, which is almost K&R).
XXXXXXXXXXXXXX X if (test) {
XXXXXX one_thing();
XXXXXX another_thing();
X }
This is the most common style used to program in Java (which is what I do all day). It's also most similar to my previous language which was a "pure block" language, requiring no "emulation". There are no single-statement bodies, blocks are inherent in the control structure syntax.
IF test THEN
oneThing;
anotherThing
END
Nothing. Indentation and other coding standards are a matter of preference.
Personal Preference I would have thought? I guess it has the code block in one vertical line so possibly easier to work out at a glance? Personally I prefer the brace to start directly under the previous line
It looks pretty standard to me. The only personal change I'd make is aligning the curly-braces with the start of the previous line, rather than the start of the next line, but that's just a personal choice.
Anyway, the style of formatting you're looking at there is a standard one for C and C++, and is used because it makes the code easier to read, and in particular by looking at the level of indentation you can tell where you are with nested loops, conditionals, etc. E.g.:
if (x == 0)
{
if (y == 2)
{
if (z == 3)
{
do_something (x);
}
}
}
OK in that example it's pretty easy to see what's happening, but if you put a lot of code inside those if statements, it can sometimes be hard to tell where you are without consistent indentation.
In your example, have a look at the position of the exit(1) statement -- if it weren't indented like that, it would be hard to tell where this was. As it is, you can tell it's at the end of that big if statement.
Code formatting is personal taste. As long as it is easy to read, it would pay for maintenance!
By following some formatting and commenting standards, first of all you show your respect to other people that will read and edit code written by you. If you don't accept rules and write somehow esoteric code the most probable result is that you will not be able communicate with other people (programmers) effectively. Code format is personal choice if software is written only by you and for you and nobody is expected to read it, but how many modern software is written only by one person ?
The "advantage" of Whitesmiths style (as the top one in your example is called) is that it mirrors the actual logical structure of the code:
indent if there is a logical dependency
place corresponding brackets on the same column so they are easy to find
opening and closing of a context (which may open/close a stack frame etc) are visible, not hidden
So, less if/else errors, loops gone wrong, and catches at the wrong level, and overall logical consistency.
But as benefactual wrote: within certain rational limits, formatting is a matter of personal preference.
Its just another style--people code how they like to code, and that is one accepted style (though not my preferred). I don't think it has much of a disadvantage or advantage over the more common style in which brackets are not indented but the code within them is. Perhaps one could justify it by saying that it more clearly delimits code blocks.
In order for this format to have "advantage", we really need some equivalent C code in another format to compare to!
Where I work, this indentation scheme is used in order to facilitate a home-grown folding editor mechanism.
Thus, I see nothing fundamentally wrong with this format - within certain rational limits, formatting is a matter of personal preference.