C macros versus inline functions
See how a macro evaluates its arguments more than once and why a function is safer.
#include <stdio.h>
#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
static inline int max_function(int a, int b) {
return a > b ? a : b;
}
static int calls = 0;
static int five(void) {
calls++;
return 5;
}
int main(void) {
int i = 3;
int from_macro = MAX_MACRO(i++, 2);
printf("macro: result %d, i = %d\n", from_macro, i);
int j = 3;
int from_function = max_function(j++, 2);
printf("function: result %d, j = %d\n", from_function, j);
calls = 0;
int m = MAX_MACRO(five(), 2);
printf("macro called five() %d times (result %d)\n", calls, m);
calls = 0;
int f = max_function(five(), 2);
printf("function called five() %d time (result %d)\n", calls, f);
return 0;
}macro: result 4, i = 5 function: result 3, j = 4 macro called five() 2 times (result 5) function called five() 1 time (result 5)
Explanation
A #define macro is text substitution done by the preprocessor before compilation. The macro's argument is pasted wherever the parameter appears, so an argument with a side effect, such as i++ or a function call, can run more than once. That is a classic source of bugs.
A static inline function evaluates each argument exactly once, has type checking, and is typically just as fast because the compiler can inline it. Use macros for things functions cannot do, such as stringizing or conditional compilation, and wrap every parameter and the whole expansion in parentheses to avoid precedence surprises.
Explore the dry run
#include <stdio.h> #define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b)) static inline int max_function(int a, int b) { return a > b ? a : b;} static int calls = 0; static int five(void) { calls++; return 5;} int main(void) { int i = 3; int from_macro = MAX_MACRO(i++, 2); printf("macro: result %d, i = %d\n", from_macro, i); int j = 3; int from_function = max_function(j++, 2); printf("function: result %d, j = %d\n", from_function, j); calls = 0; int m = MAX_MACRO(five(), 2); printf("macro called five() %d times (result %d)\n", calls, m); calls = 0; int f = max_function(five(), 2); printf("function called five() %d time (result %d)\n", calls, f); return 0;}Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| The text becomes ((i++) > (2) ? (i++) : (2)). Since 3 > 2, the second i++ runs as well. | i is incremented twice, result is 4 | — | |
| The result is 4 and i became 5, which is not what max(3, 2) should do. | i is incremented twice, result is 4 | macro: result 4, i = 5 | |
| The argument j++ is evaluated once, giving 3, and j becomes 4. | i is incremented twice, result is 4 | macro: result 4, i = 5 | |
| The function behaves as expected. | i is incremented twice, result is 4 | macro: result 4, i = 5 function: result 3, j = 4 | |
| five() is pasted into the expansion twice, so it is called twice. | i is incremented twice, result is 4 | macro: result 4, i = 5 function: result 3, j = 4 | |
| Two calls for one use. | i is incremented twice, result is 4 | macro: result 4, i = 5 function: result 3, j = 4 macro called five() 2 times (result 5) | |
| One call, as written. | i is incremented twice, result is 4 | macro: result 4, i = 5 function: result 3, j = 4 macro called five() 2 times (result 5) function called five() 1 time (result 5) |
Cost and common mistakes
O(1).
- Always parenthesize each macro parameter and the whole expansion. Without them, MAX_MACRO(a + 1, b) or 2 * MAX_MACRO(x, y) can expand into the wrong expression.
- Macros ignore types and scope, which makes compiler errors confusing. Prefer constants (enum or const) over #define for values.
- Never pass an argument with a side effect to a macro unless the macro is documented to evaluate it once.
Try it yourself
Why is the macro result 4 and not 3?
Reveal answer and reasoning
The condition i++ > 2 increments i to 4 and then the true branch evaluates i++ again, returning 4 and leaving i at 5.