3

I have some macros that are defined based on compiler flags. I'm trying to decide whether I would rather have the macro defined as (void)0 or have it undefined and cause a compile time error.

i.e.

#ifdef DEBUG
  #define PRINTF(...) printf(__VA_ARGS__)
#else
  #define PRINTF(...) (void)0
#endif

int main(void) {
  ...
  PRINTF("something");
  ...
}

vs.

#ifdef DEBUG
  #define PRINTF(...) printf(__VA_ARGS__)
#endif

int main(void) {
  ...
  #ifdef DEBUG
    PRINTF("something");
  #endif
  ...
}

I'm not sure which technique I prefer. On one hand wrapping every PRINTF statement with #ifdef's would be ugly. On the other hand it would be nice to know at compile time if I've called a function that doesn't really work in the context.

I think the deciding factor will be whether or not having the (void)0 macros is going to affect the size of the executable.

When the code is compiled, what happens to the (void)0's? If PRINTF is defined as (void)0, does that mean the executable is going to contain some sort of (void)0 instruction or will it be completely ignored?

user3817250
  • 1,003
  • 4
  • 14
  • 27
  • 2
    `(void)0` will likely not generate any code. – Tavian Barnes Jul 29 '14 at 17:08
  • If you have doubts, why don't you simply leave it out, I mean, the definition? You could really just write the `#else` definition as `#define PRINTF(...)` alone, and your lines would (hope I'm right here) simply be `;`s, i.e. empty-statements. – Utkan Gezer Jul 29 '14 at 17:15
  • What's wrong with `#define PRINTF(...) ` (you define a macro replacement as the empty string) – Brandin Jul 29 '14 at 17:15
  • Btw I think it's a bit odd to worry that `(void)0` is going to waste space in the executable. If you really want to make the executable as small as possible why are you using C – Brandin Jul 29 '14 at 17:18
  • 1
    Why do you ask a question that can only be answered for a specific compiler without saying at any time what your compiler is? – Pascal Cuoq Jul 29 '14 at 17:23
  • @Brandin for some reason I thought I read somewhere the (void)0 is the proper technique. I'm using C because I have a substantial pre-built C library to make use of. I'm just trying to minimize my part. – user3817250 Jul 29 '14 at 17:30
  • 1
    @PascalCuoq I assumed the treatment of (void)0 would be standard across c compilers. I'm using the Code Composer Studio IDE that keeps me somewhat in the dark about what compiler I'm using. – user3817250 Jul 29 '14 at 17:38
  • `#define PRINTF(...)` is fine. So is `#define PRINTF(...) (void)0` as both statements do nothing which is what you wanted. btw see here for related question/answers http://stackoverflow.com/questions/5599380/use-of-null-statement-in-c – Brandin Jul 29 '14 at 17:38
  • @ThoAppelsin see my answer for why `(void) 0` is better – ouah Jul 29 '14 at 17:56

3 Answers3

8
(void) 0;

is an expression statement with no side-effect. Any sane implementation will optimize this statement out (what else an implementation could do with such a statement?).

Having (void) 0 as a macro definition is endorsed by the C Standard as it appears in (C11) 7.2p1 for assert macro definition if NDEBUG is defined:

#define assert(ignore) ((void)0)

Note that defining:

#define PRINTF(...) (void)0

instead of

#define PRINTF(...)

has an advantage. In the first case, you have an expression (like a function that returns no value) and so it is usable for example in a comma expression or in a conditional expression.

For example:

// Comma expression
printf("test"), PRINTF("Hi Dennis");

// Conditional expression
test-expr ? perror("Hello") : PRINTF("world");

This two expression statements are only valid with the former PRINTF definition (with (void) 0).

ouah
  • 142,963
  • 15
  • 272
  • 331
5

It'll be completely ignored, you can confirm this by looking at the assembly output (gcc -S will generate file.s, the asm output), compare with and without the (void)0 line and see that it is completely the same.

Adam D. Ruppe
  • 25,382
  • 4
  • 41
  • 60
  • Is it the preprocessor or the compiler that completely ignores it? – Fiddling Bits Jul 29 '14 at 17:43
  • 1
    Neither really, I guess I should have used different wording: the preprocessor leaves in the `(void)0` and the compiler sees it (try to assign it to a variable, for example, and gcc will complain that you didn't ignore a `void` value like you are supposed to) but it doesn't actually generate any code because it is a do-nothing expression. So it is ignored in terms of code generation, the preprocessor and compiler front end both see it. – Adam D. Ruppe Jul 29 '14 at 17:53
2

A half way decent compiler will optimise away dead (unreachable) code, so you can:

  #ifdef DEBUG
  #define PRINTF(...) if (1) { printf(__VA_ARGS__) ; }
  #else
  #define PRINTF(...) if (0) { printf(__VA_ARGS__) ; }
  #endif

which has the big advantage of allowing the compiler to check the debug code, no matter whether you are working with/without your DEBUG turned on -- which reduces the risk of ending up with painful teeth marks in your backside.

  • 1
    It is a great advantage but note that it also changes the semantic, `PRINTF` is no longer a substitution for expression but now is a statement (so can no longer be used where an expression can be used). – ouah Jul 29 '14 at 18:00
  • @ouah in this case, `PRINTF` can't be used in an expression anyway, due to it possibly having the `void` type. – Drew McGowen Jul 29 '14 at 18:25
  • Why `if (0)` instead of just nothing? The compiler may remove this, but still... – Fiddling Bits Jul 29 '14 at 18:27
  • @DrewMcGowen see my answer in OP question, `void` expression can be used as operands of comma expression or conditional expression. – ouah Jul 29 '14 at 18:34
  • 1
    OK... well the `if` could be replaced by an `(1 ? printf(..) : (void)0)` ? –  Jul 29 '14 at 18:55
  • @gmch yes, it would work. (Note that you also need to cast the second operand of `?:` operator: `(1 ? (void) printf(__VA_ARGS__) : (void) 0)`) – ouah Jul 29 '14 at 21:07