Function Macros (#define)
| Since: | C89(1989) |
|---|
A macro that takes arguments. It can be used like a function, but the preprocessor performs simple text substitution before compilation. Because it is expanded inline, there is no function call overhead, and you can write generic code that is independent of types.
Syntax
#define CONSTANT_NAME value
// A function-like macro with arguments (wrap each argument and the whole expression in parentheses).
#define MACRO_NAME(arg1, arg2) ((arg1) operator (arg2))
// A multi-line macro continues each line with a backslash.
#define MACRO_NAME(x) \
do { \
statement; \
} while (0)
// The stringizing operator (#). Converts an argument into a string literal.
#define STRINGIFY(x) #x
// The token-pasting operator (##). Concatenates two tokens into one.
#define CONCAT(a, b) a##b
Function-like Macro Notation and Notes
| Notation / Operator | Description |
|---|---|
| Wrap arguments in parentheses | As in #define SQUARE(x) ((x) * (x)), wrap each argument in parentheses. This prevents incorrect behavior caused by operator precedence during expansion. |
| Wrap the whole expression in parentheses | Wrapping the entire macro expansion in parentheses prevents unintended grouping when the macro is embedded in an expression. |
| do { } while (0) | An idiom for safely writing multi-statement macros. It works correctly even when used as the body of an if statement. Without this idiom, using a macro in an if body can lead to unintended behavior. |
| # (stringizing) | Converts a macro argument into a string literal enclosed in double quotes. |
| ## (token-pasting) | Concatenates two tokens into a single identifier. |
Sample Code
sample_define_macro.c
#include <stdio.h>
/* Common function-like macros. */
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))
#define ABS(x) ((x) >= 0 ? (x) : -(x))
/* Debugging macro: stringizes the variable name with #. */
#define PRINT_INT(x) printf(#x " = %d\n", (x))
/* Multi-statement macro using do-while(0). */
#define SWAP(type, a, b) \
do { \
type _tmp = (a); \
(a) = (b); \
(b) = _tmp; \
} while (0)
/* ## pastes two tokens into one identifier. */
#define MAKE_VAR(name, num) name##num
int main(void) {
int x = 5, y = 3;
printf("MAX(%d, %d) = %d\n", x, y, MAX(x, y));
printf("MIN(%d, %d) = %d\n", x, y, MIN(x, y));
printf("SQUARE(%d) = %d\n", x, SQUARE(x));
printf("ABS(-7) = %d\n", ABS(-7));
int score = 98;
PRINT_INT(score); /* Prints "score = 98". */
printf("Before swap: x=%d, y=%d\n", x, y);
SWAP(int, x, y);
printf("After swap: x=%d, y=%d\n", x, y);
int MAKE_VAR(val, 1) = 100; /* Expands to val1 = 100. */
printf("val1 = %d\n", MAKE_VAR(val, 1));
/* Never pass side-effect expressions to macros. */
/* int a = 3; printf("%d\n", SQUARE(a++)); */ /* a is incremented twice. */
return 0;
}
Run the following command:
gcc define_macro.c -o define_macro ./define_macro MAX(5, 3) = 5 MIN(5, 3) = 3 SQUARE(5) = 25 ABS(-7) = 7 score = 98 Before swap: x=5, y=3 After swap: x=3, y=5 val1 = 100
Creating a Debug Log Macro
Combining __FILE__, __LINE__, and __func__ with a macro lets you automatically attach file name, line number, and function name to debug output.
define_debug_macro.c
#include <stdio.h>
#define LOG(fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define ASSERT(cond) \
do { \
if (!(cond)) { \
fprintf(stderr, "ASSERT FAILED: %s (%s:%d)\n", #cond, __FILE__, __LINE__); \
} \
} while (0)
int main(void) {
int hp = 100;
LOG("hp = %d", hp);
hp -= 150;
LOG("hp after damage = %d", hp);
ASSERT(hp >= 0); /* hp is negative, so assert fires */
ASSERT(hp <= 100); /* passes */
LOG("done");
return 0;
}
Run the following command:
gcc define_debug_macro.c -o define_debug_macro ./define_debug_macro [define_debug_macro.c:14] hp = 100 [define_debug_macro.c:17] hp after damage = -50 ASSERT FAILED: hp >= 0 (define_debug_macro.c:19) [define_debug_macro.c:22] done
Common Mistakes
Common Mistake: Passing Side-Effect Expressions to a Macro
Macros are text substitutions. If you pass an expression with side effects (like an increment), it may be evaluated more than once after expansion.
define_sideeffect_ng.c
#include <stdio.h>
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main(void) {
/* NG: a++ is evaluated twice, so a is incremented twice */
int a = 3;
int result = SQUARE(a++); /* expands to ((a++) * (a++)) */
printf("After SQUARE(a++): a = %d, result = %d\n", a, result);
/* a becomes 5; result is indeterminate */
/* NG: the larger value is incremented twice by MAX */
int x = 5, y = 3;
int m = MAX(x++, y++);
printf("After MAX(x++, y++): x = %d, y = %d, m = %d\n", x, y, m);
/* OK: compute the value in advance, then pass it */
int b = 3;
int sq = b * b;
printf("Correct SQUARE(3) = %d\n", sq);
return 0;
}
Run the following command:
gcc define_sideeffect_ng.c -o define_sideeffect_ng ./define_sideeffect_ng After SQUARE(a++): a = 5, result = 12 After MAX(x++, y++): x = 7, y = 4, m = 5 Correct SQUARE(3) = 9
Notes
Because function-like macros are text substitutions performed by the preprocessor, passing an expression with side effects (such as an increment) as an argument causes unintended behavior. SQUARE(a++) expands to ((a++) * (a++)), causing the increment to happen twice. From C99 onward, consider using inline functions or the type-generic _Generic facility for the same purpose.
Not wrapping arguments in parentheses leads to operator precedence issues. For example, with #define DOUBLE(x) x * 2, DOUBLE(1 + 2) expands to 1 + 2 * 2 = 5. Always wrap arguments in parentheses.
For constant macros, see #include / #define (Constants). For conditional compilation, see #ifdef / #ifndef.
If you find any errors or copyright issues, please contact us.