The other answers have already explained the issue with using plain {...}
for this purpose. Again, if you use plain {...}
inside the macro and then just habitually follow your macro invocation with ;
, as in
WRITE_INTERRUPT_MASK(0x42);
the compiler will see it as two consecutive statements: a compound statement followed by an empty statement
{ ... } // <- first statement: macro body
; // <- second statement: empty statement
Empty statement is a no-op, so this will work as long as you are using it somewhere in the middle of a plain linear sequence of statements. But if you attempt using it in an if-else
statement, like this
if (condition)
WRITE_INTERRUPT_MASK(0x42);
else
/* whatever */;
thing will start falling apart. The compiler will treat only the {...}
statement as par of the if
, while the ;
(empty statement) will be considered an independent statement that follows if
. This will make the else
part orphaned. The code will not compile.
This code
do
WRITE_INTERRUPT_MASK(0x42);
while (condition);
will not compile for exactly the same reason.
You can work around this problem by either always remembering to omit the ;
in such cases
if (condition)
WRITE_INTERRUPT_MASK(0x42)
else
/* whatever */;
or by wrapping your macro invocation into an extra pair of {...}
in such cases
if (condition) {
WRITE_INTERRUPT_MASK(0x42);
}
else
/* whatever */;
but all this is exactly what we are trying to avoid. We want a macro that behaves as a good-mannered function and not forces us to watch out for various "special contexts".
And this is where the do-while
trick comes in. In case of do-while
that trailing ;
becomes a part of the same single do-while
statement, instead of introducing a second empty statement. This is what makes it work as we want it to.