views:

356

answers:

11

Reverse engineering code and I'm kind of appalled at the style, but I wanted to make sure there's no good reason for doing these things....

Is it just me or is this a horrible coding style

if ( pwbuf ) sprintf(username,"%s",pwbuf->pw_name);
else sprintf(username,"%d",user_id);

And why wrap code not intended for compilation in an

#if 0
....
#endif

Instead of comments?


EDIT: So as some explained below, this is due to the possibility to flummox /* */ which I didn't realize.

But I still don't understand, why not just use your programming environment tools or favorite text editor's macro's to block comment it out using "//"

wouldn't this be MUCH more straightforward and easy to know to visually skip?


Am I just inexperienced in C and missing why these things might be a good idea -- or is there no excuse, and I'm justified in feeling irritated at how ugly this code is?

+10  A: 

#if 0 is used pretty frequently when the removed block contains block-comments

I won't say it's a good practice, but I see it rather often.

The single line flow-control+statement is easy enough to understand, although I personally avoid it (and most of the coding guidelines I've worked under forbid it)

BTW, I'd probably edit the title to be somewhat useful "Why use #if 0 instead of block comments"

If you have the following

#if 0
        silly();
        if(foo)
           bar();
        /* baz is a flumuxiation */
        baz = fib+3;
#endif

If you naively replace the #if 0/#endif with /* */, that will cause the comment to end right after flumuxiation, causing a syntax error when you hit the */ in the place of the #endif above..

EDIT: One final note, often the #if 0 syntax is just used while developing, particularly if you have to support multiple versions or dependencies or hardware platforms. It's not unusual for the code to be modified to

#ifdef _COMPILED_WITHOUT_FEATURE_BAZ_
    much_code();
#endif

With a centralized header defining (or not) hundreds of those #define constants. It's not the prettiest thing in the world, but every time I've worked on a decent sized project, we've used some combination of runtime switches, compile-time constants (this), compile-time compilation decisions (just use different .cpp's depending on the version), and the occasional template solution. It all depends on the details.

While you're the developer just getting the thing working in the first place, though... #if 0 is pretty common if you're not sure if the old code still has value.

jkerian
Why not just use good old fashion /* */ ??? doesn't that make it a LOT clearer that the code is NEVER going to be compiled when viewing in a text editor/coding env.? It seems like you could easily miss that preprocessor tag when scanning the code and think it was being compiled.
Jason R. Mick
A decent editor will grey out #if 0'd code as well, so that's not so much of an issue. Editing with an example.
jkerian
emacs is not a decent editor? I mean it colorizes the rest of my c-text... and it colorizes commented out code...
Jason R. Mick
@Jason You can't use /**/ to comment out other /**/, they don't nest.
Ioan
If it's a lot of code, it's not always easy to see where the comment block ends. But mostly I think it's a tradition from the times when compilers had problems with nested comments.
<insert joke about operating system without decent editor here>. Dunno... vim, SlickEdit and Visual Studio all interpet #if 0 as a comment delimiter. Considering the configuration complexity of emacs and vim though, I suspect there's a button or knob you need to turn.
jkerian
As jkerian pointed out, if the code you are commenting out already has a block quote, then adding another set of block quotes won't work. Using #if 0 is a much better way of doing it.
Torlack
@Jasaon: because `/* */` comments to not nest. So if you use them to hide code which itself contains a comment, the (outer) comment will end at the first `*/`.
James Curran
Ah makes sense... good to know...
Jason R. Mick
...but still, why not use // for block comment out. That would work, right? In emacs all you'd have to do would be define a macro and then call it a few times with ctrl+u <# times> ctrl+x e ... wouldn't that be clearer to read and know that it was commented? (you could use a reverse macro for similar purposes...)
Jason R. Mick
While this is doable in most editors, I'd suggest you reflect on the fact that "`/* */`" are "block comments" and `//` indicates a "line comment. the `#if 0` mechanism works very similar to the block comments, and it makes some sense that it would be prefered.
jkerian
Because `//` isn't (or wasn't) an official C comment, it came along with C++ -- some compilers allowed double-slash comments and some didn't. I don't know what the latest C standard says on that subject.
Stephen P
@Jason // is not a valid C89 statement, you have to use C99 or C++
Christoffer
hmm interesting. So were just /* */ allowed? How did you comment under the old spec?
Jason R. Mick
...and in that case, might the better solution be to just use a macro to insert #if 0 at the start of the line and #endif at the end of the line? ... it would be clearer then which portion was slotted for removal from compilation IMO
Jason R. Mick
Preprocessor directives have to be at the beginning of lines, so you can't comment out a single line with #if 0 / #endif
Will Dean
You can have something like `# define` though, I believe.
mathepic
Also - no good editor with gray out the code. Gray code is harder to read than syntax colored code. I don't care if I'm using it or not - I should be able to see it!
mathepic
@mathepic grey out, mark as dark blue (my preference), or whatever... commented code needs to be clearly not in use. Making `#if 0`d code easy to miss is a _terrible_ idea. The pretty syntax highlighting of my types is quite secondary to the fact that this code WILL NOT BE RUN.
jkerian
@matepicL So if I comment out a block of code for some reason or another, it should still be colored as though it would be run? Among other things, that would make finding comment tags a nightmare
Nick T
+3  A: 

That's pretty idiomatic C right there. I don't see what's so wrong with it. It's not a beautiful piece of code but it's easy to read and is clear what's going on and why, even without context.

The variable names could be better, and and it'd probably be safer to use snprintf or perhaps strncpy.

If you think it could be better, what would you prefer it look like?

I might make a slight change:

char username[32];
strncpy(username, 30, (pwbuf ? pwbuf->pw_name : user_id));
username[31] = '\0';
Steven Schlansker
I guess I would prefer to see the else logic put onto a next line. I mean I *understand* what its doing, but when I first see "else" and then what looks like a conditional, my brain kinda melts down. It just annoys me for some reason. Again maybe it isn't logical, that's why I asked the question... Maybe I have to retrain my brain to think that's a good idea?
Jason R. Mick
+1  A: 

Woah there! Don't overreact...

I would call it sloppier for more the inconsistant spacing than anything else. I have had time where I found it better to put short statements on the same line as their IF, though those statements are stretching it.

The inline style is better for vertical brevity... could easily be broken into 4, more lines

if (pwbuf) 
  sprintf(username,"%s",pwbuf->pw_name); 
else 
  sprintf(username,"%d",user_id); 

Personally I hate the next style since it so long-winded, making it difficult to skim a file.

if (pwbuf) 
{
  sprintf(username,"%s",pwbuf->pw_name); 
}
else
{ 
  sprintf(username,"%d",user_id); 
}
smdrager
The reason the extra brackets are preferred is to avoid someone adding an extra line later and thinking it will be part of the original clause.
Kenoyer130
brackets are much more future proof.
Paul Nathan
+1  A: 

Obviously, everyone has their own opinions on this sort of thing. So here's mine:

I would never write code like the above, and would think less of anyone who did. I can't count the number of times people think it's ok to get away without scope braces, and then been bitten by it.

Putting the control statement on the same line as the code block is even worse; the lack of indenting makes it harder to see the flow control whilst reading. Once you've been coding for a few years, you get used to being able to read and interpret code quickly and accurately, so long as you can rely on certain visual cues. Circumventing these cues for "special cases" means that the reader has to stop and do a double-take, for no good reason.

#if (0), on the other hand, is ok during development, but should be removed once code is "stable" (or at least replace 0 with some meaningful preprocessor symbol name).

Oli Charlesworth
When I coded primarily using vi running on an 80 column 25 line VT-100 clone terminal dialed up at 2400 baud to BSD on a VAX, each line of screen was precious, so `if (a) b(); else c();` was a lot more acceptable to me than breaking that into four lines, and the habit of dropping braces on their own lines was effectively unthinkable. Today, I'm more inclined to be generous with the vertical white space. You have to have lived within the restrictions of a real terminal to appreciate the benefits of the terser style, I suspect.
RBerteig
I'd still write code like the sample today only if it fit entirely on one line. The moment it folds to more than one, it needs the braces to avoid the obvious risks. On one line, there is less temptation to accidentally edit in extra statements.
RBerteig
I guess meaningful preprocessor symbol names are ok, as long as they are explicitly #undef'd. The trouble with things like #ifdef NOT_THIS is that one has to search through all included files to see if NOT_THIS is defined. If find an #undef NOT_THIS, you have an answer. If you don't, you're left wondering: "have I looked in all the files?". There are no such problems with #if 0.
dmuir
No. Never replace the 0 in #if 0 with anything else. Because somebody could come along and define the macro and inadvertently include the code. What you should actually do is delete the #if 0 block altogether and rely on your version control system instead.
JeremyP
@JeremyP: I don't think there's much of a problem with things like `#if (DEBUG_MODE)` or `#if (DISPLAY_GRAPHS)`, etc. for things that may still be useful to turn on or off. However, for experimental code that should no longer be needed, I agree, delete it.
Oli Charlesworth
@Oli Charlesworth: I totally agree. conditional compilation fro debug and to include specific features is not a problem. It's only commenting out of *obsolete* code that I take issue with.
JeremyP
A: 

points above noted. But monitors being widescreen and all, these days, I sort of don't mind

if (pwbuf) sprintf(username,"%s",pwbuf->pw_name);
else       sprintf(username,"%d",user_id);

Always seem to have too much horizontal space, and not enough vertical space on my screen!

Also, if the code block already has preprocessor directives, don't use #if 0; if the code already has block comments, don't use /* */. If it already has both, either resort to an editor that has a ctrl+/, to comment out lots of lines. If not, you're stuffed, delete the code outright!

Sanjay Manohar
C pedantry alert: `//` are C++ comments...
Oli Charlesworth
From the C Standard (WG14 N1124 - ISO/IEC 9899:1999; 6.4.9): "Except within a character constant, a string literal, or a comment, the characters // introduce a comment that includes all multibyte characters up to, but not including, the next new-line character."
BillP3rd
@Bill: That's C99. `//` is not part of C89 or C90, although most compilers will accept it.
Oli Charlesworth
Preprocessor block code deactivation (it's not really a comment) does nest.
Nathon
A: 
if ( pwbuf ) sprintf(username,"%s",pwbuf->pw_name);
else sprintf(username,"%d",user_id);

Idiomatic and concise. If it got touched more than 2 or 3 times, I would bracket and next-line it. It's not very maintainable if you add logging information or other conditions.

#if 0
....
#endif

Good to turn on blocks of debug code or not. Also, would avoid compilation errors related to trying to block comment this sort of thing out:

/* line comment */
...
/* line comment again */

Since C block comments don't nest.

Paul Nathan
+2  A: 

As far as block commenting using // is concerned, one reason that I can think of is that, should you check that code into your source control system, the blame log will show you as the last editor for those lines of code. While you probably want the commenting to be attributed to you, at the same time the code itself is also being attributed to you. Sure, you can go back and look at previous revisions if you need to check the blame log for the "real" author of the code, but it would save time if one preserved that information in the current revision.

Aaron Klotz
Interesting point.
Oli Charlesworth
Good point, but at the same time, if you're using source control, you probably shouldn't be commenting out dead code. You should probably be deleting it.
P Daddy
+3  A: 

Besides the problem with C-style comments not nesting, disabling blocks of code with #if 0 has the advantage of being able to be collapsed if you are using an editor that supports code folding. It is also very easy to do in any editor, whereas disabling large blocks of code with C++-style comments can be unwieldy without editor support/macros.

Also, many #if 0 blocks have an else block as well. This gives an easy way to swap between two implementations/algorithms, and is arguably less error-prone than mass-commenting out one section and mass-uncommenting another. However, you'd be better off using something more readable like #if DEBUG in that event.

bta
+2  A: 

Comments are comments. They describe the code.

Code that's being excluded from compilation is code, not comments. It will often include comments, that describe the code that isn't being compiled, for the moment/

They are two distinct concepts, and forcing the same syntax strikes me as being a mistake.

Jeff Dege
+1 This is a good rationalization. You make a good argument for *always* using the preprocessor to exclude code. Sure it's kinda ugly, but dead code *should* be ugly, right?
P Daddy
A: 

Very occasionally I use the more concise style when it supports the symmetry of code and the lines don't get too long. Take the following contrived example:

if (strcmp(s, "foo") == 0)
{
    bitmap = 0x00000001UL;
    bit = 0;
}
else if (strcmp(s, "bar") == 0)
{
    bitmap = 0x00000002UL;
    bit = 1;
}
else if (strcmp(s, "baz") == 0)
{
    bitmap = 0x00000003UL;
    bit = 2;
}
else if (strcmp(s, "qux") == 0)
{
    bitmap = 0x00000008UL;
    bit = 3;
}
else
{
    bitmap = 0;
    bit = -1;
}

and the concise version:

if      (strcmp(s, "foo") == 0) { bitmap = 0x00000001UL; bit = 0;  }
else if (strcmp(s, "bar") == 0) { bitmap = 0x00000002UL; bit = 1;  }
else if (strcmp(s, "baz") == 0) { bitmap = 0x00000003UL; bit = 2;  }
else if (strcmp(s, "qux") == 0) { bitmap = 0x00000008UL; bit = 3;  }
else                            { bitmap = 0;            bit = -1; }

Bugs are much more likely to jump straight into your face.

Disclaimer: This example is contrived, as I said. Feel free to discuss the use of strcmp, magic numbers and if a table based approach would be better. ;)

Secure
A: 

#if 0 ... #endif is pretty common in older C code. The reason is that commenting with C style comments /* .... */ doesn't work because comments don't nest.

Even though it is common, I'd say it has no place in modern code. People did it in olden days because their text editors couldn't block comment large sections automatically. More relevantly, they didn't have proper source code control as we do now. There's no excuse for leaving commented or #ifdef'd in production code.

JeremyP