Macros and its Types in C: Complete Guide
Macros and its Types in C: A Comprehensive Tutorial
Macros in C are preprocessor directives that replace code before the compilation process, helping simplify and optimize programs. They are commonly used to define constants and reusable code snippets. Macros improve code readability, reusability, and maintainability, and can enhance execution efficiency by avoiding function call overhead.
This tutorial provides an in-depth exploration of macros in C: object-like macros, chain macros, multi-line macros, function-like macros, practical applications, common pitfalls, and best practices.
Conceptual Introduction & Fundamentals
What is a Macro?
A macro is a preprocessor directive that defines a name for a piece of code or a value. Before compilation, the preprocessor replaces every occurrence of the macro name with its defined value or code.
Example:
c
#define PI 3.14159 int main(void) { double area = PI * 5 * 5; // PI is replaced with 3.14159 return 0; } Why Use Macros?
- Constants: Define named constants for readability.
- Code reuse: Create reusable code snippets.
- Performance: Avoid function call overhead for simple operations.
- Conditional compilation: Enable/disable code sections.
- Readability: Descriptive names instead of magic numbers.
- Maintainability: Change value in one place.
Macro vs. Function
| Aspect | Macro | Function |
|---|---|---|
| Processing | Preprocessor (text substitution) | Compiler (actual call) |
| Type checking | No | Yes |
| Overhead | None (inline expansion) | Function call overhead |
| Debugging | Harder | Easier |
| Side effects | Can evaluate arguments multiple times | Arguments evaluated once |
| Code size | May increase (duplication) | No duplication |
Syntax & Core Mechanics
Basic Macro Syntax
Syntax:
c
#define MACRO_NAME value
Example:
c
#include <stdio.h> #define LIMIT 5 int main(void) { printf("LIMIT: %d\n", LIMIT); return 0; } Expected output:
text
LIMIT: 5
Note: Macros don’t need to be uppercase, but it’s a convention that makes them easy to recognize.
Types of Macros
| Type | Description |
|---|---|
| Object-Like Macros | Replace name with a value or expression |
| Chain Macros | One macro expands into another macro |
| Multi-Line Macros | Span multiple lines using backslash |
| Function-Like Macros | Accept parameters like functions |
1. Object-Like Macros
The simplest type of macros. They replace the macro name with a defined value or expression.
Syntax:
c
#define MACRO_NAME value
Example:
c
#include <stdio.h> #define DATE 31 #define PI 3.14159 #define COMPANY_NAME "CoreTechED" int main(void) { printf("Lockdown will be extended upto %d-MAY-2020\n", DATE); printf("PI = %.5f\n", PI); printf("Company: %s\n", COMPANY_NAME); return 0; } Expected output:
text
Lockdown will be extended upto 31-MAY-2020 PI = 3.14159 Company: CoreTechED
2. Chain Macros
Macros that expand into other macros, creating a chain of substitutions.
Syntax:
c
#define MACRO_A MACRO_B #define MACRO_B value
Example:
c
#include <stdio.h> #define INSTAGRAM FOLLOWERS #define FOLLOWERS 138 int main(void) { printf("Geeks for Geeks have %dK followers on Instagram\n", INSTAGRAM); return 0; } Expected output:
text
Geeks for Geeks have 138K followers on Instagram
How it works:
INSTAGRAMexpands toFOLLOWERS.FOLLOWERSthen expands to138.
Example with multiple levels:
c
#include <stdio.h> #define LEVEL3 100 #define LEVEL2 LEVEL3 #define LEVEL1 LEVEL2 int main(void) { printf("Value: %d\n", LEVEL1); // Expands to 100 return 0; } Expected output:
text
Value: 100
3. Multi-Line Macros
Macros that span multiple lines for readability, using backslash (\) at the end of each line.
Syntax:
c
#define MACRO_NAME line1 \ line2 \ line3
Example:
c
#include <stdio.h> #define ELEMENTS 1, \ 2, \ 3, \ 4, \ 5 int main(void) { int arr[] = {ELEMENTS}; int count = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < count; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } Expected output:
text
1 2 3 4 5
Example with multiple statements:
c
#include <stdio.h> #define PRINT_NUMBERS(n) for (int i = 0; i < n; i++) { \ printf("%d ", i); \ } \ printf("\n"); int main(void) { PRINT_NUMBERS(5); return 0; } Expected output:
text
0 1 2 3 4
4. Function-Like Macros
Macros that accept parameters and work similarly to functions.
Syntax:
c
#define MACRO_NAME(args) expression
Example:
c
#include <stdio.h> #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define SQUARE(x) ((x) * (x)) int main(void) { int a = 18, b = 76; printf("Minimum of %d and %d: %d\n", a, b, MIN(a, b)); printf("Maximum of %d and %d: %d\n", a, b, MAX(a, b)); printf("Square of 5: %d\n", SQUARE(5)); return 0; } Expected output:
text
Minimum of 18 and 76: 18 Maximum of 18 and 76: 76 Square of 5: 25
Important: Parenthesize all arguments and the entire expression.
Full Practical Code Example with Explanation
This program demonstrates all types of macros in a realistic scenario: a geometry calculator.
Program: Macro Geometry Calculator
c
/** * macro_types_demo.c * Demonstrates all types of C macros: object-like, chain, multi-line, * and function-like macros. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> /* Object-like macros */ #define PI 3.14159 #define MAX_NAME_LEN 50 #define MAX_LINE_LEN 100 /* Chain macros */ #define SHAPE_CIRCLE CIRCLE #define CIRCLE 1 #define SHAPE_RECTANGLE RECTANGLE #define RECTANGLE 2 #define SHAPE_TRIANGLE TRIANGLE #define TRIANGLE 3 /* Multi-line macro for menu display */ #define DISPLAY_MENU do { \ printf("\n--- Geometry Calculator ---\n"); \ printf("1. Circle Area\n"); \ printf("2. Rectangle Area\n"); \ printf("3. Triangle Area\n"); \ printf("4. Exit\n"); \ printf("Enter choice: "); \ } while(0) /* Function-like macros */ #define CIRCLE_AREA(r) (PI * (r) * (r)) #define RECTANGLE_AREA(l, w) ((l) * (w)) #define TRIANGLE_AREA(b, h) (0.5 * (b) * (h)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) /* Function prototypes */ void demonstrateObjectLikeMacros(void); void demonstrateChainMacros(void); void demonstrateMultiLineMacros(void); void demonstrateFunctionLikeMacros(void); void demonstrateGeometryCalculator(void); double readNumber(const char *prompt); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Macro Types Demo ===\n"); while (1) { printf("\n--- Main Menu ---\n"); printf("1. Object-Like Macros\n"); printf("2. Chain Macros\n"); printf("3. Multi-Line Macros\n"); printf("4. Function-Like Macros\n"); printf("5. Geometry Calculator (All Types)\n"); printf("6. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstrateObjectLikeMacros(); break; case 2: demonstrateChainMacros(); break; case 3: demonstrateMultiLineMacros(); break; case 4: demonstrateFunctionLikeMacros(); break; case 5: demonstrateGeometryCalculator(); break; case 6: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Demonstrates object-like macros */ void demonstrateObjectLikeMacros(void) { printf("\n=== Object-Like Macros ===\n"); printf("PI = %.5f\n", PI); printf("MAX_NAME_LEN = %d\n", MAX_NAME_LEN); char name[MAX_NAME_LEN]; strcpy(name, "CoreTechED"); printf("Company: %s\n", name); } /* Demonstrates chain macros */ void demonstrateChainMacros(void) { printf("\n=== Chain Macros ===\n"); printf("SHAPE_CIRCLE = %d\n", SHAPE_CIRCLE); printf("SHAPE_RECTANGLE = %d\n", SHAPE_RECTANGLE); printf("SHAPE_TRIANGLE = %d\n", SHAPE_TRIANGLE); printf("CIRCLE = %d\n", CIRCLE); printf("RECTANGLE = %d\n", RECTANGLE); printf("TRIANGLE = %d\n", TRIANGLE); } /* Demonstrates multi-line macros */ void demonstrateMultiLineMacros(void) { printf("\n=== Multi-Line Macros ===\n"); /* Multi-line macro for array initialization */ #define VALUES 10, \ 20, \ 30, \ 40, \ 50 int arr[] = {VALUES}; int count = sizeof(arr) / sizeof(arr[0]); printf("Array elements: "); for (int i = 0; i < count; i++) { printf("%d ", arr[i]); } printf("\n"); /* Multi-line macro for printing */ #define PRINT_HEADER(title) do { \ printf("=====================\n"); \ printf("%s\n", title); \ printf("=====================\n"); \ } while(0) PRINT_HEADER("Multi-Line Macro Demo"); } /* Demonstrates function-like macros */ void demonstrateFunctionLikeMacros(void) { printf("\n=== Function-Like Macros ===\n"); double radius = 5.0; double length = 10.0, width = 4.0; double base = 6.0, height = 8.0; printf("Circle area (r=%.1f): %.2f\n", radius, CIRCLE_AREA(radius)); printf("Rectangle area (l=%.1f, w=%.1f): %.2f\n", length, width, RECTANGLE_AREA(length, width)); printf("Triangle area (b=%.1f, h=%.1f): %.2f\n", base, height, TRIANGLE_AREA(base, height)); printf("MAX(10, 20) = %d\n", MAX(10, 20)); printf("MIN(10, 20) = %d\n", MIN(10, 20)); } /* Demonstrates geometry calculator with all macro types */ void demonstrateGeometryCalculator(void) { printf("\n=== Geometry Calculator ===\n"); int choice; char buffer[MAX_LINE_LEN]; while (1) { DISPLAY_MENU; fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return; } choice = atoi(buffer); switch (choice) { case SHAPE_CIRCLE: { double radius = readNumber("Enter radius: "); printf("Circle area: %.2f\n", CIRCLE_AREA(radius)); break; } case SHAPE_RECTANGLE: { double length = readNumber("Enter length: "); double width = readNumber("Enter width: "); printf("Rectangle area: %.2f\n", RECTANGLE_AREA(length, width)); break; } case SHAPE_TRIANGLE: { double base = readNumber("Enter base: "); double height = readNumber("Enter height: "); printf("Triangle area: %.2f\n", TRIANGLE_AREA(base, height)); break; } case 4: printf("Returning to main menu.\n"); return; default: printf("Invalid choice. Please try again.\n"); break; } } } /* Reads a number safely */ double readNumber(const char *prompt) { char buffer[MAX_LINE_LEN]; char *endPtr; double value; while (1) { printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading input.\n"); exit(EXIT_FAILURE); } errno = 0; value = strtod(buffer, &endPtr); if (endPtr == buffer || (*endPtr != '\n' && *endPtr != '\0')) { printf("Invalid number. Please try again.\n"); continue; } if (errno == ERANGE) { printf("Number out of range. Please try again.\n"); continue; } return value; } } Explanation of Macro Types
- Object-like macros:
PI,MAX_NAME_LEN,MAX_LINE_LEN. - Chain macros:
SHAPE_CIRCLE→CIRCLE→1. - Multi-line macros:
DISPLAY_MENUusesdo { ... } while(0). - Function-like macros:
CIRCLE_AREA,RECTANGLE_AREA,TRIANGLE_AREA,MAX,MIN.
Expected Output (sample run)
text
=== Macro Types Demo === --- Main Menu --- 1. Object-Like Macros 2. Chain Macros 3. Multi-Line Macros 4. Function-Like Macros 5. Geometry Calculator (All Types) 6. Exit Enter choice: 1 === Object-Like Macros === PI = 3.14159 MAX_NAME_LEN = 50 Company: CoreTechED --- Main Menu --- ... Enter choice: 2 === Chain Macros === SHAPE_CIRCLE = 1 SHAPE_RECTANGLE = 2 SHAPE_TRIANGLE = 3 CIRCLE = 1 RECTANGLE = 2 TRIANGLE = 3 --- Main Menu --- ... Enter choice: 5 === Geometry Calculator === --- Geometry Calculator --- 1. Circle Area 2. Rectangle Area 3. Triangle Area 4. Exit Enter choice: 1 Enter radius: 5 Circle area: 78.54 --- Geometry Calculator --- ... Enter choice: 4 Returning to main menu.
Common Pitfalls, Edge Cases & Memory Considerations
1. Semicolon After Macro Definition
Adding semicolon after #define causes unexpected expansion.
c
#define MAX 100; // BUG: semicolon becomes part of macro int x = MAX; // Expands to: int x = 100;;
Solution: Do not end macro definitions with semicolons.
2. Missing Parentheses in Function-Like Macros
Missing parentheses causes precedence issues.
c
#define SQUARE(x) x * x // BUG: SQUARE(2+3) = 2 + 3 * 2 + 3 = 11
Solution: Parenthesize arguments and entire expression:
c
#define SQUARE(x) ((x) * (x))
3. Side Effects in Macro Arguments
Macros evaluate arguments multiple times, causing side effects.
c
#define SQUARE(x) ((x) * (x)) int result = SQUARE(i++); // BUG: i incremented twice
Solution: Avoid side effects in macro arguments; use inline functions.
4. Multi-Line Macro Without Backslash
Forgetting backslash at line end causes errors.
c
#define VALUES 1, 2, 3 // BUG: missing backslashes
Solution: Use \ at the end of each line except the last.
5. do { ... } while(0) Pattern
Multi-statement macros should use do { ... } while(0) to avoid issues with if-else.
c
#define PRINT_BOTH(x, y) printf("%d\n", x); printf("%d\n", y); if (condition) PRINT_BOTH(a, b); // BUG: second printf executes unconditionally Solution: Use do { ... } while(0):
c
#define PRINT_BOTH(x, y) do { \ printf("%d\n", x); \ printf("%d\n", y); \ } while(0) 6. Recursive Macros
Macros cannot be recursive.
c
#define FACT(n) ((n) <= 1 ? 1 : (n) * FACT(n-1)) // BUG: won't work
Solution: Use actual functions for recursion.
7. Macro Redefinition
Redefining a macro without #undef causes warnings.
c
#define MAX 100 #define MAX 200 // Warning: macro redefinition
Solution: Use #undef MAX before redefining.
8. Stringification and Token Pasting
Use # for stringification and ## for token pasting.
c
#define STRINGIFY(x) #x #define CONCAT(a, b) a##b printf("%s\n", STRINGIFY(hello)); // "hello" int CONCAT(my, Variable) = 10; // myVariable = 10 9. Type Safety
Macros lack type checking, which can lead to unexpected behavior.
c
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) MAX(3.14, 10); // Compares double with int
Solution: Use inline functions or _Generic (C11) for type-safe alternatives.
10. Memory Considerations
- Macros do not allocate memory; they are text substitutions.
- Function-like macros avoid function call overhead but increase code size.
- Multi-line macros with
do { ... } while(0)create a single statement. - Chain macros expand recursively until no macro names remain.
Summary & Best Practices
Macros are powerful preprocessor directives that enable code reuse, constants, and conditional compilation. Proper use improves code readability and performance.
Key Takeaways
- Object-like macros:
#define NAME value. - Chain macros: One macro expands into another.
- Multi-line macros: Use backslash (
\) at line ends. - Function-like macros: Accept parameters like functions.
- Parenthesize arguments and expressions.
- Use
do { ... } while(0)for multi-statement macros. - Avoid side effects in macro arguments.
Best Practices
- Use UPPERCASE for macro names.
- Parenthesize macro arguments and expressions.
- Avoid side effects in macro arguments.
- Use
do { ... } while(0)for multi-statement macros. - Use
#undefbefore redefining macros. - Prefer inline functions over function-like macros for type safety.
- Document complex macros with comments.
- Avoid recursive macros.
- Use stringification (
#) and token pasting (##) carefully. - Test macros with different argument types.
By mastering macros, you’ll write more efficient, readable, and maintainable C code.



