CoreTechED

Loading

Macros and its Types in C: Complete Guide

Learn C macros: object-like, chain, multi-line, and function-like macros. Master macro definitions, pitfalls, and best practices.

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

AspectMacroFunction
ProcessingPreprocessor (text substitution)Compiler (actual call)
Type checkingNoYes
OverheadNone (inline expansion)Function call overhead
DebuggingHarderEasier
Side effectsCan evaluate arguments multiple timesArguments evaluated once
Code sizeMay 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

TypeDescription
Object-Like MacrosReplace name with a value or expression
Chain MacrosOne macro expands into another macro
Multi-Line MacrosSpan multiple lines using backslash
Function-Like MacrosAccept 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:

  1. INSTAGRAM expands to FOLLOWERS.
  2. FOLLOWERS then expands to 138.

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

  1. Object-like macros: PI, MAX_NAME_LEN, MAX_LINE_LEN.
  2. Chain macros: SHAPE_CIRCLE → CIRCLE → 1.
  3. Multi-line macros: DISPLAY_MENU uses do { ... } while(0).
  4. 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

  1. Use UPPERCASE for macro names.
  2. Parenthesize macro arguments and expressions.
  3. Avoid side effects in macro arguments.
  4. Use do { ... } while(0) for multi-statement macros.
  5. Use #undef before redefining macros.
  6. Prefer inline functions over function-like macros for type safety.
  7. Document complex macros with comments.
  8. Avoid recursive macros.
  9. Use stringification (#) and token pasting (##) carefully.
  10. Test macros with different argument types.

By mastering macros, you’ll write more efficient, readable, and maintainable C code.

Leave a Reply

Your email address will not be published. Required fields are marked *