The C preprocessor is a powerful text-processing tool that runs before the compiler, transforming source code through macros, conditional compilation, and file inclusion. Preprocessor macros enable code generation, platform adaptation, debugging aids, and performance optimizations that would be impossible or cumbersome with pure C. For C programmers, mastering preprocessor macros is essential for writing portable, efficient, and maintainable code.
What are Preprocessor Macros?
Preprocessor macros are text substitutions performed by the C preprocessor before compilation begins. They are defined using the #define directive and can be simple constant replacements or function-like macros with parameters. The preprocessor also provides conditional compilation (#if, #ifdef), file inclusion (#include), and other directives that control how source code is processed.
Why Preprocessor Macros are Essential in C
- Code Generation: Create repetitive code patterns automatically
- Platform Portability: Adapt code to different operating systems and compilers
- Debugging Support: Include debug code only in development builds
- Performance: Eliminate function call overhead with inline code
- Configuration: Define compile-time constants and settings
- Code Clarity: Create expressive shortcuts for complex expressions
Basic Macro Definitions
#include <stdio.h> // ============================================================ // SIMPLE OBJECT-LIKE MACROS // ============================================================ // Constants #define PI 3.141592653589793 #define MAX_SIZE 100 #define APPLICATION_NAME "My Program" #define VERSION "1.0.0" // Simple expressions #define AREA_OF_CIRCLE(r) (PI * (r) * (r)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define ABS(x) ((x) < 0 ? -(x) : (x)) #define SQUARE(x) ((x) * (x)) #define CUBE(x) ((x) * (x) * (x)) int main() { printf("=== Simple Macro Definitions ===\n\n"); printf("APPLICATION_NAME: %s\n", APPLICATION_NAME); printf("VERSION: %s\n", VERSION); printf("PI: %f\n", PI); printf("MAX_SIZE: %d\n\n", MAX_SIZE); double radius = 5.0; printf("Circle with radius %.2f:\n", radius); printf(" Area: %.2f\n", AREA_OF_CIRCLE(radius)); int a = 10, b = 20; printf("\nMAX(%d, %d) = %d\n", a, b, MAX(a, b)); printf("MIN(%d, %d) = %d\n", a, b, MIN(a, b)); int x = -5; printf("ABS(%d) = %d\n", x, ABS(x)); printf("SQUARE(%d) = %d\n", 5, SQUARE(5)); printf("CUBE(%d) = %d\n", 3, CUBE(3)); return 0; } Function-Like Macros
#include <stdio.h> // ============================================================ // FUNCTION-LIKE MACROS // ============================================================ // Simple function-like macros #define IS_EVEN(n) ((n) % 2 == 0) #define IS_ODD(n) ((n) % 2 != 0) #define IS_POSITIVE(x) ((x) > 0) #define IS_NEGATIVE(x) ((x) < 0) // Macros with multiple statements #define PRINT_INT(x) printf(#x " = %d\n", x) #define PRINT_DOUBLE(x) printf(#x " = %f\n", x) #define PRINT_STRING(x) printf(#x " = \"%s\"\n", x) // Macros with loops #define FOR_I_FROM_TO(start, end) \ for (int i = (start); i < (end); i++) #define FOR_J_FROM_TO(start, end) \ for (int j = (start); j < (end); j++) // Macros that create temporary variables #define SWAP(a, b, type) do { \ type temp = a; \ a = b; \ b = temp; \ } while (0) // Macros with variable arguments (C99) #define DEBUG_PRINT(fmt, ...) \ printf("DEBUG [%s:%d]: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) #define ERROR_PRINT(fmt, ...) \ fprintf(stderr, "ERROR [%s:%d]: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) // Macro to compute array size #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) int main() { printf("=== Function-Like Macros ===\n\n"); int num = 42; PRINT_INT(num); PRINT_INT(100); printf("\nIS_EVEN(%d): %s\n", num, IS_EVEN(num) ? "yes" : "no"); printf("IS_ODD(%d): %s\n", num, IS_ODD(num) ? "yes" : "no"); // Using loop macro printf("\nUsing FOR_I_FROM_TO:\n"); FOR_I_FROM_TO(0, 5) { printf("i = %d\n", i); } // SWAP macro int x = 10, y = 20; printf("\nBefore swap: x = %d, y = %d\n", x, y); SWAP(x, y, int); printf("After swap: x = %d, y = %d\n", x, y); // DEBUG_PRINT with variable arguments DEBUG_PRINT("Value of num = %d", num); DEBUG_PRINT("x = %d, y = %d", x, y); // ERROR_PRINT ERROR_PRINT("Division by zero at line %d", __LINE__); // ARRAY_SIZE macro int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; printf("\nArray size: %zu\n", ARRAY_SIZE(array)); return 0; } Stringification and Token Pasting
#include <stdio.h> // ============================================================ // STRINGIFICATION (#) AND TOKEN PASTING (##) // ============================================================ // Stringification operator (#) converts parameter to string literal #define STRINGIFY(x) #x #define TO_STRING(x) STRINGIFY(x) #define PRINT_VAR(var) printf(#var " = %d\n", var) // Token pasting operator (##) concatenates tokens #define CONCAT(a, b) a##b #define MAKE_VAR(name, num) name##num // Creating variable names with token pasting #define CREATE_VARS(prefix) \ int prefix##1; \ int prefix##2; \ int prefix##3; // Generic print macro using stringification #define PRINT(x) _Generic((x), \ int: printf("%s = %d\n", #x, x), \ double: printf("%s = %f\n", #x, x), \ char*: printf("%s = %s\n", #x, x) \ ) // Macro to define structure and access functions #define DEFINE_POINT(type) \ typedef struct { \ type x; \ type y; \ } Point_##type; \ \ void print_point_##type(Point_##type *p) { \ printf(#type " point: (%f, %f)\n", (double)p->x, (double)p->y); \ } DEFINE_POINT(int) DEFINE_POINT(double) int main() { printf("=== Stringification and Token Pasting ===\n\n"); // Stringification printf("STRINGIFY(123) = %s\n", STRINGIFY(123)); printf("STRINGIFY(hello) = %s\n", STRINGIFY(hello)); printf("TO_STRING(42) = %s\n", TO_STRING(42)); // PRINT_VAR int value = 99; PRINT_VAR(value); // Token pasting int xy = 123; printf("CONCAT(x, y) = %d\n", CONCAT(x, y)); // Creating variables with token pasting CREATE_VARS(test); test1 = 10; test2 = 20; test3 = 30; printf("test1 = %d, test2 = %d, test3 = %d\n", test1, test2, test3); // Generic print int i = 42; double d = 3.14159; char *s = "Hello"; PRINT(i); PRINT(d); PRINT(s); // Using generated structures Point_int p1 = {10, 20}; Point_double p2 = {1.5, 2.5}; print_point_int(&p1); print_point_double(&p2); return 0; } Conditional Compilation
#include <stdio.h> // ============================================================ // CONDITIONAL COMPILATION // ============================================================ // Simple conditionals #define DEBUG 1 #define PLATFORM_LINUX 1 // #define PLATFORM_WINDOWS // Conditional compilation based on macros #if DEBUG #define LOG(msg) printf("LOG: %s\n", msg) #define LOG_VAR(fmt, var) printf("LOG: " fmt "\n", var) #else #define LOG(msg) ((void)0) #define LOG_VAR(fmt, var) ((void)0) #endif // Platform-specific code #ifdef PLATFORM_LINUX #include <unistd.h> #define CLEAR_SCREEN() printf("\033[2J\033[1;1H") #define SLEEP(ms) usleep((ms) * 1000) #elif defined(PLATFORM_WINDOWS) #include <windows.h> #define CLEAR_SCREEN() system("cls") #define SLEEP(ms) Sleep(ms) #else #define CLEAR_SCREEN() printf("\n\n\n") #define SLEEP(ms) /* unsupported */ #endif // Feature detection #if __STDC_VERSION__ >= 201112L #define HAVE_C11 1 #else #define HAVE_C11 0 #endif #if defined(__GNUC__) || defined(__clang__) #define COMPILER_GNU_LIKE 1 #elif defined(_MSC_VER) #define COMPILER_MSVC 1 #endif // Compiler-specific attributes #ifdef COMPILER_GNU_LIKE #define DEPRECATED(func) func __attribute__((deprecated)) #define UNUSED __attribute__((unused)) #define PACKED __attribute__((packed)) #elif defined(COMPILER_MSVC) #define DEPRECATED(func) __declspec(deprecated) func #define UNUSED #define PACKED __pragma(pack(push, 1)) #endif // Version checking #define VERSION_MAJOR 1 #define VERSION_MINOR 0 #define VERSION_PATCH 5 #define VERSION_GE(major, minor, patch) \ (VERSION_MAJOR > (major) || \ (VERSION_MAJOR == (major) && VERSION_MINOR > (minor)) || \ (VERSION_MAJOR == (major) && VERSION_MINOR == (minor) && VERSION_PATCH >= (patch))) int main() { printf("=== Conditional Compilation ===\n\n"); // Debug logging LOG("Application started"); int counter = 42; LOG_VAR("counter = %d", counter); // Platform-specific printf("Platform-specific features:\n"); #ifdef PLATFORM_LINUX printf(" Running on Linux\n"); #elif defined(PLATFORM_WINDOWS) printf(" Running on Windows\n"); #else printf(" Unknown platform\n"); #endif // Feature detection printf(" C11 support: %s\n", HAVE_C11 ? "yes" : "no"); #ifdef COMPILER_GNU_LIKE printf(" Compiler: GNU-like (gcc/clang)\n"); #elif defined(COMPILER_MSVC) printf(" Compiler: MSVC\n"); #endif // Version checking printf("\nVersion: %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); if (VERSION_GE(1, 0, 0)) { printf(" Version 1.0.0 or later\n"); } if (VERSION_GE(1, 0, 5)) { printf(" Version includes patch 5\n"); } // Sleep demonstration (if supported) printf("\nSleeping for 1 second...\n"); SLEEP(1000); printf("Done!\n"); return 0; } Include Guards and Header Management
// ============================================================ // INCLUDE GUARDS (HEADER FILE) // ============================================================ // File: mylib.h #ifndef MYLIB_H #define MYLIB_H // This prevents multiple inclusions // Macros #define MAX_BUFFER 1024 #define DEFAULT_VALUE 42 // Function declarations void init_library(void); int process_data(int input); void cleanup_library(void); // Inline functions (C99) static inline int square(int x) { return x * x; } #endif // MYLIB_H // ============================================================ // ALTERNATIVE: #pragma once (non-standard but widely supported) // ============================================================ // File: mylib2.h #pragma once // Simpler, but not standard C // Works on most compilers (gcc, clang, MSVC) #define ANOTHER_MACRO 123 // ============================================================ // DEMONSTRATION // ============================================================ // File: main.c #include <stdio.h> #include "mylib.h" #include "mylib.h" // Second inclusion does nothing due to guards #include "mylib2.h" // Demonstrate include guards int main() { printf("=== Include Guards ===\n\n"); printf("MAX_BUFFER = %d\n", MAX_BUFFER); printf("DEFAULT_VALUE = %d\n", DEFAULT_VALUE); printf("ANOTHER_MACRO = %d\n", ANOTHER_MACRO); printf("square(5) = %d\n", square(5)); return 0; } Predefined Macros
#include <stdio.h> // ============================================================ // PREDEFINED MACROS // ============================================================ int main() { printf("=== Predefined Macros ===\n\n"); // File and line information printf("__FILE__: %s\n", __FILE__); printf("__LINE__: %d\n", __LINE__); printf("__func__: %s\n", __func__); // Date and time of compilation printf("__DATE__: %s\n", __DATE__); printf("__TIME__: %s\n", __TIME__); // C standard version printf("__STDC__: %d\n", __STDC__); #ifdef __STDC_VERSION__ printf("__STDC_VERSION__: %ld\n", __STDC_VERSION__); #endif // Compiler information #ifdef __GNUC__ printf("__GNUC__: %d\n", __GNUC__); printf("__GNUC_MINOR__: %d\n", __GNUC_MINOR__); #endif #ifdef __clang__ printf("__clang__: %d\n", __clang__); printf("__clang_major__: %d\n", __clang_major__); #endif #ifdef _MSC_VER printf("_MSC_VER: %d\n", _MSC_VER); #endif // Platform detection #ifdef __linux__ printf("__linux__ defined\n"); #endif #ifdef __APPLE__ printf("__APPLE__ defined\n"); #endif #ifdef _WIN32 printf("_WIN32 defined\n"); #endif // Endianness #ifdef __BYTE_ORDER__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ printf("Little-endian\n"); #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ printf("Big-endian\n"); #endif #endif return 0; } Advanced Macro Techniques
#include <stdio.h> // ============================================================ // ADVANCED MACRO TECHNIQUES // ============================================================ // 1. X-Macros (for generating code from a list) #define COLORS \ X(RED, "FF0000") \ X(GREEN, "00FF00") \ X(BLUE, "0000FF") \ X(BLACK, "000000") \ X(WHITE, "FFFFFF") // Generate enum #define X(name, hex) name, typedef enum { COLORS COLOR_COUNT } Color; #undef X // Generate string array #define X(name, hex) #name, const char *color_names[] = { COLORS }; #undef X // Generate hex code array #define X(name, hex) hex, const char *color_hex[] = { COLORS }; #undef X // Generate switch-case #define X(name, hex) \ case name: return #name; const char* color_to_string(Color c) { switch (c) { COLORS default: return "UNKNOWN"; } } #undef X // 2. Macro overloading (varying number of arguments) #define GET_MACRO(_1, _2, _3, _4, NAME, ...) NAME // 1 argument #define PRINT_1(a) printf("%d\n", a) // 2 arguments #define PRINT_2(a, b) printf("%d %d\n", a, b) // 3 arguments #define PRINT_3(a, b, c) printf("%d %d %d\n", a, b, c) #define PRINT(...) \ GET_MACRO(__VA_ARGS__, PRINT_3, PRINT_2, PRINT_1)(__VA_ARGS__) // 3. Recursive macros (with workarounds) #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) #define EVAL4(...) __VA_ARGS__ #define MAP_OUT // 4. Assert macro with message #define ASSERT(expr) \ do { \ if (!(expr)) { \ fprintf(stderr, "Assertion failed: %s at %s:%d\n", \ #expr, __FILE__, __LINE__); \ abort(); \ } \ } while (0) // 5. Compile-time assertions (C11 _Static_assert) #define STATIC_ASSERT(expr, msg) \ _Static_assert(expr, msg) // 6. Logging with different levels #define LOG_LEVEL_NONE 0 #define LOG_LEVEL_ERROR 1 #define LOG_LEVEL_WARN 2 #define LOG_LEVEL_INFO 3 #define LOG_LEVEL_DEBUG 4 #ifndef LOG_LEVEL #define LOG_LEVEL LOG_LEVEL_INFO #endif #define LOG_ERROR(fmt, ...) \ do { if (LOG_LEVEL >= LOG_LEVEL_ERROR) \ fprintf(stderr, "ERROR: " fmt "\n", __VA_ARGS__); \ } while (0) #define LOG_WARN(fmt, ...) \ do { if (LOG_LEVEL >= LOG_LEVEL_WARN) \ fprintf(stderr, "WARN: " fmt "\n", __VA_ARGS__); \ } while (0) #define LOG_INFO(fmt, ...) \ do { if (LOG_LEVEL >= LOG_LEVEL_INFO) \ printf("INFO: " fmt "\n", __VA_ARGS__); \ } while (0) #define LOG_DEBUG(fmt, ...) \ do { if (LOG_LEVEL >= LOG_LEVEL_DEBUG) \ printf("DEBUG: " fmt "\n", __VA_ARGS__); \ } while (0) int main() { printf("=== Advanced Macro Techniques ===\n\n"); // X-Macros printf("X-Macros example:\n"); for (int i = 0; i < COLOR_COUNT; i++) { printf(" %s = #%s\n", color_names[i], color_hex[i]); } printf("\ncolor_to_string(RED): %s\n", color_to_string(RED)); // Overloaded macros printf("\nOverloaded PRINT macro:\n"); PRINT(10); PRINT(20, 30); PRINT(40, 50, 60); // Assert macros int x = 10; ASSERT(x > 0); // OK // Compile-time assertion STATIC_ASSERT(sizeof(int) >= 4, "int must be at least 4 bytes"); // Logging macros printf("\nLogging with levels:\n"); LOG_ERROR("Division by zero at %d", __LINE__); LOG_WARN("Low memory: %d bytes free", 1024); LOG_INFO("Application started"); LOG_DEBUG("x = %d", x); return 0; } Debugging Macros
#include <stdio.h> #include <stdlib.h> // ============================================================ // DEBUGGING MACROS // ============================================================ // Trace macro - prints function entry/exit #define TRACE() \ printf("TRACE: %s() at %s:%d\n", __func__, __FILE__, __LINE__) #define TRACE_ENTER() \ printf("--> %s()\n", __func__) #define TRACE_EXIT() \ printf("<-- %s()\n", __func__) #define TRACE_EXIT_VAL(val) \ printf("<-- %s() returning %d\n", __func__, val); \ return val // Memory debugging #define MALLOC(size) \ malloc_with_info(size, __FILE__, __LINE__) #define FREE(ptr) \ free_with_info(ptr, __FILE__, __LINE__) void* malloc_with_info(size_t size, const char *file, int line) { void *ptr = malloc(size); printf("MALLOC: %zu bytes at %p from %s:%d\n", size, ptr, file, line); return ptr; } void free_with_info(void *ptr, const char *file, int line) { printf("FREE: %p from %s:%d\n", ptr, file, line); free(ptr); } // Variable dump #define DUMP_INT(x) printf(#x " = %d (at %s:%d)\n", x, __FILE__, __LINE__) #define DUMP_PTR(x) printf(#x " = %p (at %s:%d)\n", x, __FILE__, __LINE__) #define DUMP_STRING(x) printf(#x " = \"%s\" (at %s:%d)\n", x, __FILE__, __LINE__) // Generic dump using _Generic #define DUMP(x) _Generic((x), \ int: DUMP_INT, \ double: DUMP_DOUBLE, \ char*: DUMP_STRING, \ void*: DUMP_PTR \ )(x) // Array dump #define DUMP_ARRAY(arr, size) \ do { \ printf(#arr " (%zu elements): ", size); \ for (size_t _i = 0; _i < (size); _i++) { \ printf("%d ", arr[_i]); \ } \ printf("\n"); \ } while (0) // Breakpoint macro (if supported) #ifdef __GNUC__ #define BREAKPOINT() __asm__("int $3") #elif defined(_MSC_VER) #define BREAKPOINT() __debugbreak() #else #define BREAKPOINT() ((void)0) #endif // Example functions for tracing int factorial(int n) { TRACE_ENTER(); if (n <= 1) { TRACE_EXIT_VAL(1); } int result = n * factorial(n - 1); TRACE_EXIT_VAL(result); return result; // Won't be reached due to TRACE_EXIT_VAL } // Better approach for tracing int factorial2(int n) { TRACE_ENTER(); if (n <= 1) { TRACE_EXIT(); return 1; } int result = n * factorial2(n - 1); TRACE_EXIT(); return result; } int main() { printf("=== Debugging Macros ===\n\n"); // Trace example printf("Factorial trace:\n"); int result = factorial2(5); printf("Result: %d\n\n", result); // Memory debugging printf("Memory debugging:\n"); int *p = MALLOC(10 * sizeof(int)); for (int i = 0; i < 10; i++) { p[i] = i * i; } FREE(p); // Dump macros printf("\nDump macros:\n"); int x = 42; double y = 3.14; char *str = "Hello"; int arr[] = {1, 2, 3, 4, 5}; DUMP_INT(x); DUMP_STRING(str); DUMP_ARRAY(arr, 5); return 0; } Common Pitfalls and Best Practices
#include <stdio.h> // ============================================================ // COMMON PITFALLS // ============================================================ // PITFALL 1: Missing parentheses #define BAD_SQUARE(x) x * x #define GOOD_SQUARE(x) ((x) * (x)) // PITFALL 2: Multiple evaluation of arguments #define BAD_MAX(a, b) ((a) > (b) ? (a) : (b)) // GOOD_MAX defined above // PITFALL 3: Side effects in macro arguments #define INC_AND_MAX(a, b) ((a) > (b) ? (a) : (b)) // PITFALL 4: Semicolon in macro definition #define BAD_PRINT(msg) printf("%s\n", msg); // Don't put semicolon here #define GOOD_PRINT(msg) printf("%s\n", msg) // PITFALL 5: Macro without do-while for multiple statements #define BAD_SWAP(a, b, type) \ type temp = a; \ a = b; \ b = temp #define GOOD_SWAP(a, b, type) \ do { \ type temp = a; \ a = b; \ b = temp; \ } while (0) // PITFALL 6: Macro names that conflict #define max(a, b) ((a) > (b) ? (a) : (b)) // May conflict with stdlib.h // PITFALL 7: Forgetting to undef temporary macros #define TEMP_MACRO(x) ((x) * 2) // ============================================================ // BEST PRACTICES // ============================================================ // Best Practice 1: Uppercase macro names #define MAX_BUFFER_SIZE 1024 #define FOREACH(i, n) for (int i = 0; i < (n); i++) // Best Practice 2: Parenthesize everything #define MULTIPLY(a, b) ((a) * (b)) // Best Practice 3: Use do-while for multi-statement macros #define SAFE_SWAP(a, b, type) \ do { \ type temp = a; \ a = b; \ b = temp; \ } while (0) // Best Practice 4: Avoid side effects in macro arguments #define SAFE_MAX(a, b) \ ({ __typeof__(a) _a = (a); \ __typeof__(b) _b = (b); \ _a > _b ? _a : _b; }) // Best Practice 5: Use inline functions when possible static inline int max_inline(int a, int b) { return a > b ? a : b; } // Best Practice 6: Document macros clearly /** * Compute the square of a number * @param x The number to square * @return x * x */ #define SQUARE(x) ((x) * (x)) // Best Practice 7: Use #undef for temporary macros #define CREATE_LIST(name, size) \ int name[size]; \ for (int i = 0; i < size; i++) name[i] = 0 // Later, undefine if no longer needed #undef CREATE_LIST int main() { printf("=== Common Pitfalls and Best Practices ===\n\n"); // Pitfall 1 demonstration printf("Pitfall 1 - Missing parentheses:\n"); printf(" BAD_SQUARE(3+2) = %d (expected 25)\n", BAD_SQUARE(3+2)); printf(" GOOD_SQUARE(3+2) = %d\n\n", GOOD_SQUARE(3+2)); // Pitfall 3 demonstration printf("Pitfall 3 - Side effects:\n"); int a = 5, b = 3; int result = INC_AND_MAX(a++, b); printf(" a was incremented twice! a=%d, result=%d\n\n", a, result); // Pitfall 4 demonstration printf("Pitfall 4 - Semicolon issues:\n"); if (1) BAD_PRINT("This prints"); printf("But this always prints because of semicolon!\n\n"); // Pitfall 5 demonstration printf("Pitfall 5 - Multi-statement macros:\n"); int x = 10, y = 20; printf(" Before BAD_SWAP: x=%d, y=%d\n", x, y); if (x > 0) BAD_SWAP(x, y, int); // Only first statement executes in if printf(" After BAD_SWAP: x=%d, y=%d (didn't swap properly)\n", x, y); x = 10, y = 20; printf(" Before GOOD_SWAP: x=%d, y=%d\n", x, y); if (x > 0) GOOD_SWAP(x, y, int); printf(" After GOOD_SWAP: x=%d, y=%d\n\n", x, y); // Best Practice: Use inline functions when possible printf("Best Practice - Inline functions:\n"); printf(" max_inline(10, 20) = %d\n", max_inline(10, 20)); return 0; } Macro vs Inline Functions Comparison
#include <stdio.h> #include <time.h> // ============================================================ // MACRO VS INLINE FUNCTION COMPARISON // ============================================================ // Macro version #define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b)) // Inline function version static inline int max_inline(int a, int b) { return a > b ? a : b; } // Generic inline (C11) static inline double max_double(double a, double b) { return a > b ? a : b; } // Macro with type safety issues #define ADD_MACRO(a, b) ((a) + (b)) // Type-safe inline static inline int add_int(int a, int b) { return a + b; } static inline double add_double(double a, double b) { return a + b; } // Performance comparison void performance_test() { const int ITERATIONS = 100000000; int a = 5, b = 10; int result = 0; clock_t start, end; printf("=== Performance: Macro vs Inline ===\n\n"); // Macro start = clock(); for (int i = 0; i < ITERATIONS; i++) { result += MAX_MACRO(a, b); } end = clock(); double macro_time = (double)(end - start) / CLOCKS_PER_SEC; // Inline start = clock(); for (int i = 0; i < ITERATIONS; i++) { result += max_inline(a, b); } end = clock(); double inline_time = (double)(end - start) / CLOCKS_PER_SEC; printf("Macro time: %.3f seconds\n", macro_time); printf("Inline time: %.3f seconds\n", inline_time); printf("Difference: %.2f%%\n", (macro_time - inline_time) / macro_time * 100); printf("Result (prevents optimization): %d\n\n", result); } int main() { printf("=== Macro vs Inline Function Comparison ===\n\n"); // Type safety printf("Type safety:\n"); printf(" MAX_MACRO(5, 10) = %d\n", MAX_MACRO(5, 10)); printf(" MAX_MACRO(5.5, 10.5) = %d (type conversion!)\n", MAX_MACRO(5.5, 10.5)); printf(" max_inline(5, 10) = %d\n", max_inline(5, 10)); // printf(" max_inline(5.5, 10.5) = %d\n", max_inline(5.5, 10.5)); // Error printf(" max_double(5.5, 10.5) = %.1f\n\n", max_double(5.5, 10.5)); // Debugging printf("Debugging:\n"); printf(" Macros are harder to debug (no step into)\n"); printf(" Inline functions work with debugger\n\n"); // Code size printf("Code size:\n"); printf(" Macros may cause code bloat if used many times\n"); printf(" Inline functions similar, but compiler decides\n\n"); // When to use each: printf("When to use macros:\n"); printf(" - Simple constant definitions\n"); printf(" - Conditional compilation\n"); printf(" - Code generation (X-macros)\n"); printf(" - When you need __FILE__, __LINE__\n\n"); printf("When to use inline functions:\n"); printf(" - Type-safe operations\n"); printf(" - Complex logic\n"); printf(" - Debugging support\n"); printf(" - When performance matters less than safety\n"); performance_test(); return 0; } Summary Table
| Directive | Purpose | Example |
|---|---|---|
#define | Define macro | #define PI 3.14159 |
#undef | Remove macro | #undef PI |
#if | Conditional | #if DEBUG |
#ifdef | If defined | #ifdef __linux__ |
#ifndef | If not defined | #ifndef HEADER_H |
#else | Else clause | #else |
#elif | Else if | #elif defined(__APPLE__) |
#endif | End conditional | #endif |
#include | Include file | #include <stdio.h> |
#error | Error message | #error "Unsupported platform" |
#pragma | Compiler-specific | #pragma once |
#line | Line control | #line 100 "newfile.c" |
# | Stringify | #x becomes "x" |
## | Token paste | a##b becomes ab |
Conclusion
Preprocessor macros are a powerful feature in C that enable code generation, conditional compilation, and platform adaptation. Key takeaways:
- Object-like macros provide named constants and simple substitutions
- Function-like macros can inline code but require careful parentheses
- Conditional compilation adapts code to different platforms and configurations
- Stringification and token pasting enable advanced code generation techniques
- X-macros provide a way to generate repetitive code from a single list
- Include guards prevent multiple inclusion of header files
Best practices:
- Always parenthesize macro parameters and the entire macro body
- Use uppercase names for macros to distinguish them from functions
- Avoid side effects in macro arguments
- Use
do { ... } while (0)for multi-statement macros - Consider inline functions instead of macros for type-safe operations
- Document macros clearly, especially complex ones
#undeftemporary macros after use
Common pitfalls to avoid:
- Missing parentheses leading to operator precedence issues
- Multiple evaluation of arguments with side effects
- Semicolons in macro definitions
- Macro name conflicts with standard library
- Not handling multi-statement macros correctly in conditionals
Mastering preprocessor macros elevates C programming from simple code writing to sophisticated code generation, enabling solutions that are portable, efficient, and maintainable across different platforms and configurations.
Complete C Programming Guide + Compilers Collection
1. C srand() Function – Understanding Seed Initialization
https://macronepal.com/understanding-the-c-srand-function
Explains how srand() initializes the pseudo-random number generator in C by setting a seed value. Using the same seed produces the same sequence, while time(NULL) gives different results each run.
2. C rand() Function Mechanics and Limitations
https://macronepal.com/c-rand-function-mechanics-and-limitations
Explains how rand() generates pseudo-random numbers between 0 and RAND_MAX, its deterministic nature, and limitations for security use cases.
3. C log() Function
https://macronepal.com/c-log-function-2
Covers natural logarithm calculation using <math.h> and its applications.
4. Mastering Date and Time in C
https://macronepal.com/mastering-date-and-time-in-c
Explains <time.h> functions like time(), clock(), difftime(), and struct tm.
5. Mastering time_t Type in C
https://macronepal.com/mastering-the-c-time_t-type-for-time-management
Explains time representation as seconds since Unix epoch and conversion functions.
6. C exp() Function
https://macronepal.com/c-exp-function-mechanics-and-implementation
Explains exponential function exp(x) and its scientific applications.
7. C log() Function (Alternate Guide)
https://macronepal.com/c-log-function
Comparison of log() and log10() with usage examples.
8. C log10() Function
https://macronepal.com/mastering-the-log10-function-in-c
Explains base-10 logarithm for engineering and scientific applications.
9. C tan() Function
https://macronepal.com/understanding-the-c-tan-function
Explains tangent function and radian-based calculations.
10. Random Numbers in C (Secure vs Predictable)
https://macronepal.com/mastering-c-random-numbers-for-secure-and-predictable-applications
Explains difference between rand() and secure randomness methods.
11. Free Online C Compiler
https://macronepal.com/free-online-c-code-compiler-2
Browser-based compiler for testing C programs instantly.
C Functions, Arguments, Parameters & Flow
Mastering Functions in C – Complete Guide
https://macronepal.com/c/mastering-functions-in-c-a-complete-guide/
Covers function structure, modular programming, and real-world usage.
Function Arguments in C
https://macronepal.com/c-function-arguments/
Explains how arguments are passed and used in function calls.
Function Parameters in C
https://macronepal.com/c-function-parameters/
Explains defining inputs for functions and matching them with arguments.
Function Declarations in C
https://macronepal.com/c-function-declarations-syntax-rules-and-best-practices/
Covers prototypes, syntax rules, and best practices.
Function Calls in C
https://macronepal.com/understanding-function-calls-in-c-syntax-mechanics-and-best-practices/
Explains execution flow and parameter handling during function calls.
Void Functions in C
https://macronepal.com/understanding-void-functions-in-c-syntax-patterns-and-best-practices/
Explains functions that do not return values.
Return Values in C
https://macronepal.com/c-return-values-mechanics-types-and-best-practices/
Explains different return types and how functions return results.
Pass-by-Value in C
https://macronepal.com/aws/understanding-pass-by-value-in-c-mechanics-implications-and-best-practices/
Explains how copies of variables are passed into functions.
Pass-by-Reference in C
https://macronepal.com/c/understanding-pass-by-reference-in-c-pointers-semantics-and-safe-practices/
Explains using pointers to modify original variables.
C strstr() Function
https://macronepal.com/aws/c-strstr-function/
Explains substring search inside strings in C.
C Preprocessor & Macros
https://macronepal.com/mastering-c-variadic-macros-for-flexible-debugging/
https://macronepal.com/mastering-the-stdc-macro-in-c/
https://macronepal.com/c-time-macro-mechanics-and-usage/
https://macronepal.com/understanding-the-c-date-macro/
https://macronepal.com/c-file-type/
https://macronepal.com/mastering-c-line-macro-for-debugging-and-diagnostics/
https://macronepal.com/mastering-predefined-macros-in-c/
https://macronepal.com/c-error-directive-mechanics-and-usage/
https://macronepal.com/understanding-the-c-pragma-directive/
https://macronepal.com/c-include-directive/
C Structures, Memory, Scope & Linkage
https://macronepal.com/mastering-structures-in-c/
https://macronepal.com/c-structure-declaration-mechanics-and-usage/
https://macronepal.com/c-structure-initialization-mechanics-and-best-practices/
https://macronepal.com/mastering-c-structure-member-access-for-reliable-data-handling/
https://macronepal.com/c-nested-structures/
https://macronepal.com/mastering-arrays-of-structures-in-c/
https://macronepal.com/c-structure-pointers-mechanics-and-implementation/
https://macronepal.com/understanding-c-structure-parameter-passing-mechanics/
https://macronepal.com/mastering-c-returning-structures-for-efficient-data-flow/
https://macronepal.com/c-self-referential-structures/
https://macronepal.com/mastering-structure-alignment-in-c/
https://macronepal.com/c-structure-padding-mechanics-and-optimization/
https://macronepal.com/understanding-c-flexible-array-members-mechanics-and-usage/
https://macronepal.com/mastering-c-anonymous-structures-for-flattened-data-layouts/
https://macronepal.com/c-unions/
https://macronepal.com/mastering-c-name-mangling-and-symbol-decoration/
https://macronepal.com/c-no-linkage-mechanics-and-scope-isolation/
https://macronepal.com/understanding-c-internal-linkage-mechanics-and-architecture/
C Scope, Storage Classes & Typedef
https://macronepal.com/mastering-function-prototype-scope-in-c/
https://macronepal.com/c-function-scope-mechanics-and-visibility/
https://macronepal.com/understanding-c-file-scope-mechanics-and-architecture/
https://macronepal.com/mastering-c-scope-rules-for-predictable-name-resolution/
https://macronepal.com/c-scope-rules/
https://macronepal.com/mastering-c-register-storage-class-for-historical-context-and-modern-alternatives/
https://macronepal.com/mastering-_thread_local-in-c/
https://macronepal.com/c-extern-storage-class-mechanics-and-usage/
https://macronepal.com/understanding-the-c-static-storage-class-mechanics-and-usage/
https://macronepal.com/c-auto-storage-class/
https://macronepal.com/c-typedef-with-pointers/
Extra Articles
https://macronepal.com/13757-2/
https://macronepal.com/13748-2/
https://macronepal.com/13747-2/
https://macronepal.com/13746-2/
https://macronepal.com/13745-2/
https://macronepal.com/13708-2/
https://macronepal.com/13707-2/
https://macronepal.com/13702-2/
Online Compilers
https://macronepal.com/free-html-online-code-compiler/
https://macronepal.com/free-online-python-code-compiler/
https://macronepal.com/free-online-python2-code-compiler/
https://macronepal.com/free-online-java-code-compiler/
https://macronepal.com/free-online-javascript-code-compiler/
https://macronepal.com/free-online-node-js-code-compiler/
https://macronepal.com/free-online-c-code-compiler/
https://macronepal.com/free-online-c-code-compiler-2/
https://macronepal.com/free-online-c-code-compiler-3/
https://macronepal.com/free-online-php-code-compiler/
https://macronepal.com/free-online-ruby-code-compiler/
https://macronepal.com/free-online-perl-code-compiler/
https://macronepal.com/free-online-lua-code-compiler/
https://macronepal.com/free-online-tcl-code-compiler/
https://macronepal.com/free-online-groovy-code-compiler/
https://macronepal.com/free-online-j-shell-code-compiler/
https://macronepal.com/free-online-haskell-code-compiler/
https://macronepal.com/free-online-scala-code-compiler/
https://macronepal.com/free-online-common-lisp-code-compiler/
https://macronepal.com/free-online-d-code-compiler/
https://macronepal.com/free-online-ada-code-compiler/
https://macronepal.com/free-erlang-code-compiler/
https://macronepal.com/free-online-assembly-code-compiler/
