1

I recently saw a piece of C code including a macro of the following style:

#define TOTO()              \
do {                        \
  do_something();           \
  do_another_something();   \
} while (0)

I was at first wondering of the purpose of the do while (0) here, but this answer explained to me: it is in case the macro is used just after an if or else without curly brackets, like this:

if (something)
    TOTO();
else
    do_something_else();

So here, without the do while (0) statement, the code would be expanded to:

if (something)
    do_something();
    do_another_something();
else
    do_something_else();

Which is syntactically wrong, because the else is not directly following an if scope anymore.

But I thought it would work as well by declaring the macro in its own scope, without necessary a do while around it, so I tested the same code with just curly brackets. My entire code looks like this:

#include <stdio.h>

#define HELLO_WORLD()       \
{                           \
    printf("hello ");       \
    printf("world!\n");     \
}

int     main(int argc, char** argv)
{
    if (argc == 1)
        HELLO_WORLD();
    else
        fprintf(stderr, "nope\n");
    return 0;
}

But GCC give me the following error:

error: ‘else’ without a previous ‘if’

However, the code of the main function should be expanded to:

if (argc == 1)
    {
        printf("hello ");
        printf("world!\n");
    }
else
    fprintf(stderr, "nope\n");
return 0;

Which is valid.

So what am I missing here?

Community
  • 1
  • 1
Aracthor
  • 5,757
  • 6
  • 31
  • 59
  • 3
    The semicolon you have put after `HELLO_WORLD()` –  Jan 15 '16 at 09:20
  • 1
    As always when facing macro-related problems: your compiler may have a switch to output the preprocessed file, so you can see what the actual input to the code compiler looks like. – Jongware Jan 15 '16 at 09:21
  • @Downvoters How is this a bad question? It is a duplicate, yes, but then mark it as one at least instead of downvoting and telling everybody it's a bad question. Or what's the reason for this? – cadaniluk Jan 15 '16 at 09:23
  • 1
    The first part `do{} while()` part has nothing to do with the his code I think . ( However I didn't downvote I just think about the reason ) –  Jan 15 '16 at 09:24
  • 1
    The whole do-while-zero macro trick was always kind of retarded, because it assumes that code without braces is good and should be used. The proper solution is to adopt a coding standard which always enforces braces after each and every statement, and then check the code with static analysis to ensure that it does. – Lundin Jan 15 '16 at 09:32
  • For that reason the `{ }` macro is **better** practice than `do {} while(0)`, because it creates a compiler error if you don't have any braces in the caller. – Lundin Jan 15 '16 at 09:33
  • @Aracthor No! `if(something) { { stuff(); } ; } else` is perfectly fine C code, as opposed to `if(something) { stuff(); } ; else`. Where `{ stuff(); }` is your expanded macro. – Lundin Jan 15 '16 at 09:39

2 Answers2

9

It's the semicolon after the macro.

The macro is expanded to this instead:

if (argc == 1)
    {
        printf("hello ");       \
        printf("world!\n");     \
    };
else        // HERE, SYNTAX ERROR
    fprintf(stderr, "nope\n");
return 0;

Since there should be no semicolon between the body of an if-statement and an else-clause, it's a syntax error.
OTOH, a do-while loop allows (and needs) a semicolon.


The misconception regarding the compiler output can be easily avoided by just printing the real preprocessor output. That's possible by using

  • the -E switch of the gcc executable. From man 1 gcc:

-E Stop after the preprocessing stage; do not run the compiler proper. The output is in the form of preprocessed source code, which is sent to the standard output.

  • the preprocessor directly in shape of the cpp executable.

Thanks to
- @dhke for correcting the line the error occurs at.
- @kakeh for the suggestion to view the preprocessor output beforehand.

cadaniluk
  • 15,027
  • 2
  • 39
  • 67
2

Look CAREFULLY at how you call the macro.

You'd have to write

if (argc == 1)
    HELLO_WORLD() // NO SEMICOLON!!!!!!!
else
    fprintf(stderr, "nope\n");

which would be godawful because you could never replace the HELLO_WORLD macro with a real function.

gnasher729
  • 51,477
  • 5
  • 75
  • 98