C Notes
Complete guide to #define macros in C — defining constants, function-like macros, macro pitfalls, multiline macros, stringification, token pasting, common patterns and best practices.
The #define directive is the preprocessor's workhorse. It creates macros — named text substitutions that are expanded before compilation. From simple constants to complex function-like patterns, macros are powerful but need careful handling to avoid subtle bugs.
Simple Constants (Object-like Macros)
#define PI 3.14159265358979
#define MAX_SIZE 1000
#define AUTHOR "Dennis Ritchie"
#define TRUE 1
#define FALSE 0These perform simple text replacement:
#include <stdio.h>
#define TAX_RATE 0.18
#define COMPANY "TechCorp"
#define VERSION 2
int main() {
double price = 1000.0;
double tax = price * TAX_RATE;
double total = price + tax;
printf("%s Invoice (v%d)\n", COMPANY, VERSION);
printf("Price: $%.2f\n", price);
printf("Tax (%.0f%%): $%.2f\n", TAX_RATE * 100, tax);
printf("Total: $%.2f\n", total);
return 0;
}TechCorp Invoice (v2) Price: $1000.00 Tax (18%): $180.00 Total: $1180.00
#define vs const — When to Use Which
| Feature | #define | const |
|---|---|---|
| Processed by | Preprocessor | Compiler |
| Type checking | None | Yes |
| Memory | No storage | Variable in memory |
| Scope | Global (until #undef) | Follows C scope |
| Debugger | Shows value only | Shows name and value |
| Can be used for | Anything (types, code) | Values only |
| Array sizes (C89) | Yes | No (use VLA or #define) |
Function-like Macros
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define SWAP(a, b, type) { type temp = (a); (a) = (b); (b) = temp; }#include <stdio.h>
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define CLAMP(val, lo, hi) (MAX(lo, MIN(val, hi)))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int main() {
printf("SQUARE(5) = %d\n", SQUARE(5));
printf("MAX(10, 20) = %d\n", MAX(10, 20));
printf("CLAMP(150, 0, 100) = %d\n", CLAMP(150, 0, 100));
printf("CLAMP(-5, 0, 100) = %d\n", CLAMP(-5, 0, 100));
return 0;
}SQUARE(5) = 25 MAX(10, 20) = 20 CLAMP(150, 0, 100) = 100 CLAMP(-5, 0, 100) = 0
The Parentheses Problem — #1 Macro Pitfall
// WRONG — missing parentheses
#define DOUBLE(x) x * 2
int result = DOUBLE(3 + 1);
// Expands to: 3 + 1 * 2 = 3 + 2 = 5 (WRONG! Expected 8)
// CORRECT — always wrap parameters AND the whole expression
#define DOUBLE(x) ((x) * 2)
int result = DOUBLE(3 + 1);
// Expands to: ((3 + 1) * 2) = 8 (CORRECT)Rule: Always parenthesize:
- Each parameter usage:
(x) - The entire macro body:
((x) * (x))
The Double-Evaluation Problem
result = 6, x = 7, y = 4
Lesson: Never pass expressions with side effects (++, --, function calls) to function-like macros.
Multiline Macros
======================== My Program ======================== [INFO] main.c:13 - Program started [WARN] main.c:14 - Low memory
Stringification (#) and Token Pasting (##)
#include <stdio.h>
// # converts argument to string literal
#define STRINGIFY(x) #x
#define PRINT_EXPR(expr) printf(#expr " = %d\n", (expr))
// ## concatenates tokens to form new identifiers
#define DECLARE_VAR(type, name) type var_##name
#define GETTER(field) get_##field()
int main() {
printf("%s\n", STRINGIFY(Hello World)); // "Hello World"
int x = 10, y = 20;
PRINT_EXPR(x + y); // printf("x + y" " = %d\n", (x + y));
PRINT_EXPR(x * y);
DECLARE_VAR(int, count) = 42; // int var_count = 42;
printf("var_count = %d\n", var_count);
return 0;
}Hello World x + y = 30 x * y = 200 var_count = 42
#undef — Removing a Macro
#define BUFFER_SIZE 1024
// ... use BUFFER_SIZE ...
#undef BUFFER_SIZE // Remove the definition
#define BUFFER_SIZE 4096 // Redefine with new valueConditional Definition
// Define only if not already defined (safe default)
#ifndef MAX_RETRIES
#define MAX_RETRIES 3
#endif
// Allow override from command line:
// gcc -DMAX_RETRIES=5 program.cCommon Macro Patterns
Interview Questions
Q1: Why must you parenthesize macro parameters?
Without parentheses, operator precedence can produce wrong results. #define MUL(a,b) a*b — calling MUL(2+3, 4) expands to 2+3*4 = 14 (not 20). With ((a)*(b)) it correctly gives ((2+3)*(4)) = 20.
Q2: What is the double-evaluation problem in macros?
When a macro argument has side effects (like x++), it gets evaluated multiple times because it appears multiple times in the expansion. MAX(x++, y) might increment x twice. Functions don't have this problem because arguments are evaluated once.
Q3: What's the difference between #define and typedef?
#define is text substitution (preprocessor), typedef creates a type alias (compiler). #define can replace any text, typedef only creates type names. typedef respects scope and handles pointers correctly. Use typedef for types, #define for constants and code patterns.
Q4: How do you write a safe multiline macro?
Wrap it in do { ... } while(0). This makes the macro behave like a single statement, works correctly with if/else without braces, and requires a semicolon after use — just like a function call.
Summary
#defineperforms text substitution before compilation- Always parenthesize parameters AND the full macro body
- Beware of double-evaluation with side-effect arguments
- Use
do { ... } while(0)for multiline macros #stringifies,##concatenates tokens- Prefer
constfor type-safe numeric constants - Use
#definewhen you need compile-time array sizes, platform macros, or code generation
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for #define Macros in C – Constants and Macro Functions.
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, define, macros, #define macros in c – constants and macro functions
Related C Programming Topics