C Notes
Complete guide to macro functions in C — parameterized macros, variadic macros, do-while trick, inline comparison with macros, safe macro patterns, debugging macros, practical examples.
Function-like macros look like function calls but are expanded by the preprocessor as inline text. They have zero overhead (no function call), work with any type (generic), but come with pitfalls that require careful engineering. This guide covers advanced macro techniques and safe patterns.
Basic Parameterized Macros
#define ADD(a, b) ((a) + (b))
#define SQUARE(x) ((x) * (x))
#define IS_EVEN(n) (((n) % 2) == 0)
#define IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
#define TO_LOWER(c) ((c) + 32)Circle area (r=5): 78.54 100°C = 212.0°F 2024 leap? Yes 2023 leap? No
The do { ... } while(0) Trick
The most important technique for multiline macros:
// PROBLEM: This macro breaks with if/else
#define BAD_SWAP(a, b) { int t = (a); (a) = (b); (b) = t; }
if (condition)
BAD_SWAP(x, y); // Expands to { ... }; ← extra semicolon!
else // ERROR: "else without matching if"
something_else();
// SOLUTION: do-while(0) wrapping
#define SWAP(a, b) do { \
int t = (a); \
(a) = (b); \
(b) = t; \
} while(0)
if (condition)
SWAP(x, y); // Works perfectly!
else
something_else();Why it works:
do { ... } while(0)is a single statement- The semicolon after the macro call terminates the do-while
- No dangling semicolons that break if/else
Type-Generic Macros
Macros work with any type — they're like templates:
#include <stdio.h>
// Works with int, float, double, char...
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
// Type-safe swap using typeof (GCC extension)
#define TYPED_SWAP(a, b) do { \
typeof(a) _temp = (a); \
(a) = (b); \
(b) = _temp; \
} while(0)
int main() {
// Works with integers
printf("MAX(3, 7) = %d\n", MAX(3, 7));
// Works with floats
printf("MIN(3.14, 2.71) = %.2f\n", MIN(3.14, 2.71));
// Works with chars
printf("MAX('a', 'z') = %c\n", MAX('a', 'z'));
int x = 10, y = 20;
TYPED_SWAP(x, y);
printf("After swap: x=%d, y=%d\n", x, y);
return 0;
}MAX(3, 7) = 7
MIN(3.14, 2.71) = 2.71
MAX('a', 'z') = z
After swap: x=20, y=10Variadic Macros (__VA_ARGS__)
Macros that accept a variable number of arguments (C99):
[INFO ] Server started with 42 users [WARN ] Memory at 80% capacity [ERROR] Connection timeout after 99.9s [DEBUG main.c:19] Variable users = 42
The ##__VA_ARGS__ (GCC extension) removes the trailing comma when no extra arguments are passed.
Macro vs Inline Function
| Feature | Macro | inline Function |
|---|---|---|
| Type checking | None | Full |
| Side effects | Double evaluation | Single evaluation |
| Debugging | Hard (expanded text) | Normal |
| Code size | Duplicated everywhere | Compiler decides |
| Type generic | Naturally | No (needs separate functions) |
| Recursion | Not possible | Possible |
| Overhead | Zero | Zero (if truly inlined) |
// Macro version — type generic, but unsafe with side effects
#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
// Inline version — type safe, but only for int
static inline int max_inline(int a, int b) {
return a > b ? a : b;
}Advanced Pattern: Assert Macro
#include <stdio.h>
#include <stdlib.h>
#define ASSERT(condition) do { \
if (!(condition)) { \
fprintf(stderr, "ASSERTION FAILED: %s\n", #condition); \
fprintf(stderr, " File: %s, Line: %d\n", __FILE__, __LINE__); \
abort(); \
} \
} while(0)
int main() {
int x = 5;
ASSERT(x > 0); // Passes
ASSERT(x < 10); // Passes
// ASSERT(x > 100); // Would print: ASSERTION FAILED: x > 100
printf("All assertions passed!\n");
return 0;
}All assertions passed!
Advanced Pattern: Error Handling
Allocated and used successfully: 42
Macro for Unit Testing
FAIL: add(2, 2) == 5 (got 4, expected 5) at line 28 Results: 4 passed, 1 failed
Interview Questions
Q1: Why use do { ... } while(0) for multiline macros?
It makes the macro behave like a single statement that requires a semicolon. Without it, macros in if/else without braces break because of dangling semicolons or missing statement grouping. The while(0) is always false so the loop runs exactly once.
Q2: What is __VA_ARGS__ and when do you use it?
__VA_ARGS__ represents the variable arguments passed to a variadic macro (defined with ...). It's used for printf-like macros: #define LOG(fmt, ...) printf(fmt, __VA_ARGS__). The ## prefix handles the case when no variable arguments are passed (removes trailing comma).
Q3: When should you use a macro instead of an inline function?
Use macros when you need type-generic behavior (works with any type without overloads), compile-time code generation (token pasting, stringification), or when the pattern involves multiple statements that reference the caller's scope (like a SWAP that modifies the caller's variables).
Q4: How do you prevent naming conflicts in macro variables?
Use unusual names with underscores or prefixes: _temp_##__LINE__. In GCC, use __typeof__ and statement expressions ({...}) for safe temporary variables. Some codebases use __macro_var_ prefixes to avoid conflicts with user code.
Summary
- Function-like macros provide zero-overhead inline expansion
- Always wrap in
do { ... } while(0)for multiline safety - Always parenthesize parameters and the entire expression
- Variadic macros (
__VA_ARGS__) enable printf-like interfaces - Use
#for stringification and##for token pasting - Macros are type-generic but have the double-evaluation pitfall
- Prefer inline functions when type safety matters more than genericity
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Macro Functions in C – Parameterized Macros.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this C Programming topic.
Search Terms
c-programming, c programming, programming, preprocessor, macro, functions, macro functions in c – parameterized macros
Related C Programming Topics