Question
I have been seeing the pattern do { ... } while (0) for many years, especially inside #define macros in C. I assume it is useful for creating an inner scope for variable declarations and for allowing break statements instead of goto.
What is this pattern actually used for? Is it useful for anything else? Do developers still use it in practice?
Example pattern:
#define LOG(msg) do { \
printf("%s\n", msg); \
} while (0)
Short Answer
By the end of this page, you will understand why do { ... } while (0) is commonly used in C macros, what problem it solves, how it makes multi-statement macros behave like a single statement, and when it is appropriate to use in real code.
Concept
The do { ... } while (0) pattern in C is mainly used to wrap multiple macro statements so they behave like one single statement.
Without this wrapper, a macro that expands to several statements can break surrounding control flow, especially inside if/else blocks.
Why this matters
In C, the preprocessor performs text substitution. That means a macro is not a function. It is pasted directly into your code before compilation.
If a macro contains more than one statement, this can cause surprising syntax problems.
For example:
#define BAD_MACRO(x) printf("%d\n", x); x++
Used like this:
if (n > 0)
BAD_MACRO(n);
else
printf("done\n");
The expansion becomes:
if (n > 0)
printf("%d\n", n); n++;
else
printf("done\n");
Now the if only controls the first statement, and the no longer matches correctly.
Mental Model
Think of a macro as a piece of paper with code written on it. The C preprocessor cuts it out and pastes it directly into your program.
If the paper contains several loose statements, they can spill into surrounding code and cause confusion.
do { ... } while (0) is like putting those statements into a sealed envelope labeled "treat this as one statement".
That envelope:
- keeps all the statements together,
- lets you place a semicolon after it,
- avoids breaking nearby
if/elselogic, - gives temporary variables a local scope.
So the pattern is not about looping. It is about grouping and safety.
Syntax and Examples
The common syntax is:
#define MACRO_NAME(args) do { \
/* one or more statements */ \
} while (0)
Example: unsafe macro
#define SET_ZERO_AND_PRINT(x) x = 0; printf("reset\n")
Using it:
if (ready)
SET_ZERO_AND_PRINT(value);
else
printf("not ready\n");
This is dangerous because the macro expands into two separate statements.
Safe version
#define SET_ZERO_AND_PRINT(x) do { \
x = 0; \
printf("reset\n"); \
} while (0)
Now it behaves like a single statement.
Example with local variables
#define SWAP(a, b) do { \
int temp = (a); \
(a) = (b); \
(b) = temp; \
} while (0)
Explanation:
- The macro can declare
tempsafely inside its own block.
Step by Step Execution
Consider this macro:
#define PRINT_PAIR(a, b) do { \
printf("%d %d\n", (a), (b)); \
printf("sum = %d\n", (a) + (b)); \
} while (0)
Used here:
int x = 2;
int y = 3;
if (x < y)
PRINT_PAIR(x, y);
else
printf("no output\n");
Expansion idea
The preprocessor roughly turns it into:
if (x < y)
do {
printf("%d %d\n", (x), (y));
printf("sum = %d\n", (x) + (y));
} while (0);
else
printf("no output\n");
Step by step
x < yis checked.- Since
2 < 3is true, thedoblock starts. printf("%d %d\n", x, y);prints .
Real World Use Cases
do { ... } while (0) is most useful in C codebases that rely on macros for reusable statement blocks.
Common use cases
- Logging macros
- Add file names, line numbers, or debug prefixes.
- Error-handling macros
- Check conditions and print diagnostics.
- Resource cleanup helpers
- Group several cleanup steps in one macro.
- Assertion-style macros
- Validate assumptions in debug builds.
- Embedded systems and kernels
- Macros are often preferred for portability or low-level control.
Example: debug logging
#define DEBUG_LOG(msg) do { \
fprintf(stderr, "DEBUG: %s\n", msg); \
} while (0)
Example: guarded action
#define CHECK_AND_RETURN(ptr) do { \
if ((ptr) == NULL) { \
fprintf(stderr, "null pointer\n"); \
return; \
} \
} while (0)
These patterns are practical because they keep call sites compact while avoiding common macro syntax bugs.
Real Codebase Usage
In real C projects, this pattern appears mostly in multi-statement macros.
Why teams use it
- To make macro calls look like ordinary statements
- To avoid broken
if/elsecontrol flow - To provide a local scope for temporary variables
- To support consistent semicolon usage at call sites
Common patterns
Guard-style macros
#define RETURN_IF_NULL(p) do { \
if ((p) == NULL) \
return; \
} while (0)
Error-reporting macros
#define FAIL_IF(cond, msg) do { \
if (cond) { \
fprintf(stderr, "%s\n", msg); \
return -1; \
} \
} while (0)
Scoped temporary work
#define CLAMP_TO_ZERO(x) do { \
if ((x) < 0) { \
(x) = 0; \
} \
} while (0)
Important note
Many modern C codebases prefer functions over macros when possible, because functions are type-checked and easier to debug. But when a macro truly needs multiple statements, do { ... } while (0) remains a standard and widely recognized technique.
Common Mistakes
1. Using a multi-statement macro without a wrapper
Broken:
#define BAD(x) printf("%d\n", x); x++
Problem:
- It expands into separate statements.
- It can break
if/elsecode.
Fix:
#define GOOD(x) do { \
printf("%d\n", x); \
x++; \
} while (0)
2. Forgetting that macros do text substitution
Broken:
#define SQUARE(x) x * x
int result = SQUARE(1 + 2);
This becomes:
int result = 1 + 2 * 1 + 2;
Fix:
#define SQUARE(x) ((x) * (x))
Even with do { ... } while (0), expression-like macros still need parentheses.
Comparisons
| Technique | Best for | Behaves like one statement? | Has local scope? | Type checked? |
|---|---|---|---|---|
| Plain multi-statement macro | Quick but unsafe macro expansion | No | No | No |
do { ... } while (0) macro | Safe multi-statement macro | Yes | Yes | No |
Block { ... } alone in macro | Grouping statements only | Not reliably with trailing semicolon | Yes | No |
| Function | Reusable runtime behavior | Yes | Yes | Yes |
do { ... } while (0) vs
Cheat Sheet
#define NAME(args) do { \
/* statements */ \
} while (0)
Purpose
- Make a multi-statement macro behave like one statement
- Allow safe use in
if/else - Provide local scope for temporary variables
- Allow a natural trailing semicolon at the call site
Typical use
#define LOG(msg) do { \
fprintf(stderr, "%s\n", msg); \
} while (0)
Safe call style
LOG("hello");
Why not just use { ... }?
- A bare block does not integrate as cleanly with statement syntax.
do { ... } while (0)is the conventional safe macro wrapper.
Important rules
- Do not put the final semicolon inside the macro definition.
- Parenthesize macro arguments where needed.
- Remember: macros are text substitution, not functions.
breakexits the wrapper, not an outer loop.
Good fit
FAQ
Why is do { ... } while (0) used in C macros?
It makes a multi-statement macro behave like a single statement, which prevents syntax problems in places like if/else blocks.
Does do { ... } while (0) actually loop?
No. Because the condition is 0, the body runs exactly once.
Why not just use braces { ... } in the macro?
A plain block groups statements, but do { ... } while (0) works more reliably as a single statement with a trailing semicolon.
Is this pattern only for macros?
Mostly, yes. It is primarily a macro-safety pattern. Outside macros, it is much less common.
Can I declare variables inside do { ... } while (0)?
Yes. That is one useful benefit. Variables declared inside the block stay scoped to that macro body.
Can break be useful inside this pattern?
Yes. It can be used to exit the macro body early. But it only exits the wrapper, not any surrounding loop.
Should I use a function instead of this macro pattern?
If a function can do the job, usually yes. Functions are type-checked and easier to debug. Use this pattern when you specifically need a multi-statement macro.
Do developers still use this today?
Yes. It remains common in C codebases, especially system code, embedded code, and projects that rely heavily on macros.
Mini Project
Description
Create a small C debugging helper using macros. The project demonstrates why do { ... } while (0) is useful for wrapping multiple statements safely, especially when the macro is used inside if/else blocks.
Goal
Build and use safe multi-statement macros for logging and validation in a small C program.
Requirements
- Create a logging macro that prints a label and a value.
- Create a validation macro that checks for
NULLand returns early. - Use both macros inside
if/elseor function logic. - Ensure the macros use
do { ... } while (0). - Show that the program compiles and runs correctly.
Keep learning
Related questions
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.
C Pointer to Array vs Array of Pointers: How to Read Complex Declarations
Learn the difference between pointer-to-array and array-of-pointers in C, plus a simple rule for reading complex declarations correctly.