Object-like & Function-like Macros
The preprocessor (`cpp`) runs before the compiler, performing textual substitution. `#define PI 3.14159` replaces every occurrence of `PI` with the literal text. Function-like macros look like functions but are expanded inline — they have no type safety, no scope, and no evaluation guarantees. Every professional C codebase uses both, and every professional C programmer knows exactly where they break.
Macros — Safe Patterns & Classic Traps
#include <stdio.h>
/* ── Object-like macros ──────────────────────────────────── */
#define MAX_BUF_SIZE 4096
#define VERSION_MAJOR 2
#define VERSION_MINOR 1
#define PI 3.14159265358979323846
/* ── Function-like macros — syntax pitfalls ──────────────── */
/* WRONG: no parens — wrong precedence */
#define BAD_DOUBLE(x) x * 2
/* BAD_DOUBLE(3+4) = 3+4*2 = 11, not 14! */
/* CORRECT: wrap argument AND result in parens */
#define DOUBLE(x) ((x) * 2)
#define SQUARE(x) ((x) * (x))
/* SQUARE(x++) — still bad: x incremented TWICE! */
/* ── Multi-statement macro — do/while(0) trick ─────────────
Allows the macro to be used safely in if/else without braces:
if (cond) DEBUG_PRINT("msg"); ← single statement, no brace needed */
#define DEBUG_PRINT(msg) do { fprintf(stderr, "[DEBUG %s:%d] %s
", __FILE__, __LINE__, (msg)); } while (0)
/* ── Stringify (#) and token-paste (##) ─────────────────── */
#define STRINGIFY(x) #x
#define CONCAT(a, b) a##b
/* ── Predefined macros ──────────────────────────────────── */
void show_predefined(void) {
printf("File: %s
", __FILE__); /* current filename */
printf("Line: %d
", __LINE__); /* current line number */
printf("Date: %s
", __DATE__); /* compilation date */
printf("Time: %s
", __TIME__); /* compilation time */
/* __func__ in C99: current function name */
printf("Func: %s
", __func__);
}
int main(void) {
int x = 5;
printf("DOUBLE(3+4) = %d
", DOUBLE(3+4)); /* 14 — correct */
/* BAD_DOUBLE demonstration */
printf("BAD_DOUBLE(3+4) = %d
", BAD_DOUBLE(3+4)); /* 11 — wrong */
DEBUG_PRINT("entering main");
/* Stringify */
printf("%s
", STRINGIFY(MAX_BUF_SIZE)); /* "MAX_BUF_SIZE" */
printf("%s
", STRINGIFY(hello world)); /* "hello world" */
/* Token paste */
int xy = 42; /* CONCAT(x,y) → xy */
printf("xy = %d
", CONCAT(x,y)); /* 42 */
show_predefined();
printf("Version: %d.%d
", VERSION_MAJOR, VERSION_MINOR);
printf("PI = %.10f
", PI);
(void)x;
return 0;
}Common Mistakes
- Missing parentheses in function-like macros — `#define ABS(x) x < 0 ? -x : x` → `ABS(a-b)` = `a-b < 0 ? -a-b : a-b` — wrong. Wrap: `#define ABS(x) ((x) < 0 ? -(x) : (x))`.
- Macro argument evaluated multiple times — `SQUARE(x++)` expands to `((x++) * (x++))` — x is incremented twice. Never pass expressions with side effects to function-like macros.
- No semicolon after single-statement macros in if/else — `if (cond) ASSERT_ZERO(n);` — if the macro expands to `if (n != 0) abort()`, the outer `else` attaches to the inner `if`. Use `do { ... } while (0)`.
Tip
Tip
Practice Objectlike Functionlike Macros in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Understanding memory layout is essential for efficient C programming
Practice Task
Note
Practice Task — (1) Write a working example of Objectlike Functionlike Macros from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Objectlike Functionlike Macros is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready c code.
Key Takeaways
- The preprocessor (`cpp`) runs before the compiler, performing textual substitution.
- Missing parentheses in function-like macros — `#define ABS(x) x < 0 ? -x : x` → `ABS(a-b)` = `a-b < 0 ? -a-b : a-b` — wrong. Wrap: `#define ABS(x) ((x) < 0 ? -(x) : (x))`.
- Macro argument evaluated multiple times — `SQUARE(x++)` expands to `((x++) * (x++))` — x is incremented twice. Never pass expressions with side effects to function-like macros.
- No semicolon after single-statement macros in if/else — `if (cond) ASSERT_ZERO(n);` — if the macro expands to `if (n != 0) abort()`, the outer `else` attaches to the inner `if`. Use `do { ... } while (0)`.