30

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?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jason R. Mick
  • 5,177
  • 4
  • 40
  • 69
  • I've done that before, for some reason it seems clearer. Never seen preprocessor directives for commenting code out. – Novikov Sep 02 '10 at 19:42
  • @Novikov: I've seen that use of preprocessor directives a lot :D – BoltClock Sep 02 '10 at 19:49
  • 5
    It's kinda ugly, but not full-out *fugly*. It has the chance to look much better with only light-to-moderate beer goggles. – FrustratedWithFormsDesigner Sep 02 '10 at 19:50
  • 1
    I've done that preprocessor directive thing. The advantage is that it maintains your code formatting and color coding. It's also easier to turn the block of code on than comments. – nmichaels Sep 02 '10 at 20:34
  • Wearing my grumpy old codger hat for a moment, one has to remember that a fair amount of C idiom (including `#if 0`) dates back to the early days. We used terminals with a limited number of total characters, no graphics, no windows, and only one text color because the CRT had only one phosphor. Editors capable enough to do block comment out intelligently were real scarce. – RBerteig Sep 03 '10 at 00:08
  • 1
    It also minimizes diffs and makes patches infinitely easier to read. – Dan Bechard Jun 07 '17 at 21:04

13 Answers13

38

#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
  • 16,497
  • 3
  • 46
  • 59
  • 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 Sep 02 '10 at 19:45
  • 5
    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 Sep 02 '10 at 19:46
  • 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 Sep 02 '10 at 19:47
  • 9
    @Jason You can't use /**/ to comment out other /**/, they don't nest. – Ioan Sep 02 '10 at 19:49
  • 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. – user328543 Sep 02 '10 at 19:49
  • . 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 Sep 02 '10 at 19:50
  • 2
    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 Sep 02 '10 at 19:51
  • 3
    @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 Sep 02 '10 at 19:51
  • ...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 Sep 02 '10 at 20:00
  • 1
    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 Sep 02 '10 at 20:03
  • 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 Sep 02 '10 at 20:04
  • 1
    @Jason // is not a valid C89 statement, you have to use C99 or C++ – Christoffer Sep 02 '10 at 20:05
  • hmm interesting. So were just /* */ allowed? How did you comment under the old spec? – Jason R. Mick Sep 02 '10 at 20:08
  • ...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 Sep 02 '10 at 20:10
  • 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 Sep 02 '10 at 20:12
  • You can have something like `# define` though, I believe. – alternative Sep 02 '10 at 20:23
  • 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! – alternative Sep 02 '10 at 21:19
  • @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 Sep 02 '10 at 21:35
  • @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 Sep 02 '10 at 22:10
25

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.


I'm editing this because I'm in the middle of a sizeable refactor and I'm making heavy use of this pattern.

As a part of this refactor, I'm removing some widely-used types, and replacing them with another. The result, of course, is that nothing will build.

And I really hate spending days fixing one issue after another in the hope that when I'm done everything will build and all the tests will run.

So my first step is to #ifdef-out all the code that won't compile, and then to [Ignore] all the unit tests that call it. With this done everything builds and all the non-ignored tests pass.

The result is a lot of functions that look like this:

public void MyFunction()
{
#if true
    throw new NotImplementedException("JT-123");
#else
    // all the existing code that won't compile
#endif
}

Then I unignore the unit tests, one at a time, and then fix the functions, one at a time.

It's going to take me a couple of days to worth through all of it, and all of these #if's will be gone, before I create the pull request to merge this, but I find it helpful, during the process.

Jeff Dege
  • 11,190
  • 22
  • 96
  • 165
  • 6
    +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 Sep 03 '10 at 15:31
  • One might make the point that commenting out code which isn't used (no matter whether it's done with comments or preprocessor directives) is a misuse because that's the task of a SCM. In case you want to preserve an old version of a peace of code (e.g. in order to avoid making a mistake twice) this code definitely becomes a static comment (code mixed with helpful explanations) which no longer has anything to do with the code of your program. That given it'd always be wrong to exclude code from compilation. – Kalle Richter Aug 29 '15 at 17:11
  • I won't say always, but in general, I agree with you. I think proper practice would be to clean up this sort of mess before check-in. – Jeff Dege Aug 30 '15 at 12:39
7

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
  • 43,959
  • 6
  • 69
  • 99
4

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
  • 11,287
  • 1
  • 28
  • 22
  • 1
    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 Sep 03 '10 at 15:33
3

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
  • 37,580
  • 14
  • 81
  • 100
  • 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 Sep 02 '10 at 19:49
  • In this case the "slight change" won't work at all, because `user_id` is an `int` and needs to be rendered using `%d`. – Steve Summit Jul 09 '21 at 11:14
2

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).

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • 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 Sep 03 '10 at 00:03
  • 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 Sep 03 '10 at 00:05
  • 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 Sep 03 '10 at 09:33
  • 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 Sep 03 '10 at 10:52
  • @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. – Oliver Charlesworth Sep 03 '10 at 11:33
  • @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 Sep 03 '10 at 13:27
0

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
  • 7,327
  • 6
  • 39
  • 49
0

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
  • 6,920
  • 3
  • 35
  • 58
  • 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 Sep 02 '10 at 20:25
  • @Bill: That's C99. `//` is not part of C89 or C90, although most compilers will accept it. – Oliver Charlesworth Sep 02 '10 at 20:31
  • Preprocessor block code deactivation (it's not really a comment) does nest. – nmichaels Sep 02 '10 at 20:46
  • 1
    As nmichaels said, preprocessor if blocks can nest. Preprocessor `#if` blocks should be documented with `/* */` comments (or `//` comments if you prefer, and have a C99-compatible compiler, but that can throw people off) for clarity anyways, so `#if 0` blocks shouldn't make things any less readable if they're properly commented. – Justin Time - Reinstate Monica Jan 27 '16 at 22:37
0
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
  • 39,638
  • 28
  • 112
  • 212
0

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
  • 4,268
  • 1
  • 18
  • 16
0

#if is a macro which checks for the condition written aside to it, since ‘0’ represents a false, it means that the block of code written in between ‘#if 0’ and ‘#endif’ will not be compiled and hence can be treated as comments.

So, we can basically say that #if 0 is used to write comments in a program.

Example :

#if 0
int a;
int b;
int c = a + b;
#endif

The section written between “#if 0” and “#endif” are considered as comments.

Questions arises : “/* … */” can be used to write comments in a program then why ”#if 0”?

Answer : It is because, #if 0 can be used for nested comments but nested comments are not supported by “/* … */”

What are nested comments? Nested Comments mean comments under comments and can be used in various cases like :

Let us take an example that you have written a code like below:

enter image description here

Now, someone is reviewing your code and wants to comment this whole piece of code in your program because, he doesn’t feel the need for this piece of code. A common approach to do that will be :

enter image description here

The above is an example of nested comments.The problem with the above code is, as soon as the first “/” after “/” is encountered, the comment ends there. i.e., in the above example, the statement : int d = a-b; is not commented.

This is solved by using “if 0” :

enter image description here

Here, we have used nested comments using #if 0.

Bhawna
  • 26
  • 3
0

I can name a few reasons for using #if 0:

  • comments don't nest, #if direcitves do;

  • It is more convenient: If you want to temporarily enable a disabled block of code, with #if 0 you just have to put 1 instead of 0. With /* */ you have to remove both /* and */;

  • you can put a meaningful macro instead of 0, like ENABLE_FEATURE_FOO;

  • automated formatting tools will format code inside #if block, but ignore commented out code;

  • it is easier to grep for #if than look for comments;

  • it plays nicer with VCS because you aren't touching the original code, just adding lines around it.

Hedede
  • 1,133
  • 13
  • 27
-1

#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
  • 84,577
  • 15
  • 123
  • 161