C Programming Preprocessor Directives
Essential Concepts

C Preprocessor Directives - Complete Guide

Master C preprocessor directives including #include, #define, macros, conditional compilation (#ifdef, #ifndef), and programming best practices.

4 Directive Types

Complete coverage

Practical Examples

Real-world usage

Macro Functions

Powerful text substitution

Introduction to C Preprocessor

The C preprocessor is a tool that processes your source code before it's compiled. It handles directives (lines starting with #) to perform text manipulation, file inclusion, and conditional compilation.

Preprocessor Phases:
Source Code
Preprocessing
Compilation
Linking
Executable
Why Use Preprocessor?
  • Code Reusability: Include standard and custom headers
  • Constants: Define symbolic constants
  • Macros: Create inline functions
  • Conditional Compilation: Platform-specific code
  • Debugging: Include/exclude debug code
Preprocessor Directives
  • #include: File inclusion
  • #define: Macro definition
  • #ifdef/#ifndef: Conditional compilation
  • #if/#else/#endif: Conditional blocks
  • #pragma: Compiler-specific instructions
  • #error: Force compilation error

Important Preprocessor Facts

Preprocessor directives begin with # and are processed before compilation. They are not C statements (no semicolon needed). The preprocessor performs text substitution - it doesn't understand C syntax, only manipulates text.

C Preprocessor Directives Comparison

Here is a comprehensive comparison of all preprocessor directives in C with their key characteristics:

Directive Syntax Purpose Common Uses
#include
File Inclusion
#include 
#include "filename"
  • Include header files
  • Insert file contents
  • Reuse code declarations
Standard headers Custom headers
<> for system, "" for local files
#define
Macro Definition
#define CONSTANT value
#define MACRO(args) expansion
  • Define constants
  • Create macros
  • Text substitution
Constants Inline functions
No type checking, pure text replacement
#ifdef/#ifndef
Conditional Compilation
#ifdef MACRO
    // code if defined
#endif

#ifndef MACRO
    // code if not defined
#endif
  • Platform-specific code
  • Feature toggles
  • Debug code inclusion
Portability Debug builds
Compile different code based on definitions
#pragma
Compiler Specific
#pragma directive_name
#pragma once
#pragma pack(1)
  • Compiler instructions
  • Optimization hints
  • Memory alignment
Compiler-specific Non-portable
Implementation-defined behavior
Choosing the Right Directive: Use #include for reusing code, #define for constants and simple macros, #ifdef/#ifndef for conditional compilation, and #pragma sparingly for compiler-specific needs.

The #include Directive - Complete Guide

The #include directive tells the preprocessor to insert the contents of another file into the current file. This is essential for including header files that contain function declarations, macros, and type definitions.

Syntax:
#include <filename> // System headers #include "filename" // User headers

Search Order:

#include <filename>
System directories
File found/not found
#include "filename"
Current directory
System directories
File found/not found

Key Characteristics:

  • Angle brackets (<>): Search system/include directories only
  • Double quotes (""): Search current directory first, then system directories
  • No semicolon: Preprocessor directives don't end with semicolons
  • Text insertion: File contents are literally inserted at the #include location

#include Directive Examples:

Example 1: Standard Headers
// System headers - use angle brackets
#include <stdio.h>    // Standard I/O functions
#include <stdlib.h>   // Standard library functions
#include <string.h>   // String manipulation functions
#include <math.h>     // Mathematical functions
#include <time.h>     // Time and date functions

// User headers - use double quotes
#include "myheader.h"  // Your custom header file
#include "../utils.h"  // Header in parent directory
#include "config.h"    // Configuration header

int main() {
    printf("Hello, Preprocessor!\n");
    return 0;
}
Example 2: Custom Header File Structure
/********** config.h **********/
#ifndef CONFIG_H
#define CONFIG_H

// Configuration constants
#define MAX_USERS 100
#define BUFFER_SIZE 1024
#define DEBUG_MODE 1
#define VERSION "1.0.0"

#endif // CONFIG_H

/********** utils.h **********/
#ifndef UTILS_H
#define UTILS_H

// Function declarations
void print_banner();
int calculate_sum(int a, int b);
void log_message(const char* message);

#endif // UTILS_H

/********** main.c **********/
#include <stdio.h>
#include "config.h"
#include "utils.h"

int main() {
    printf("Program Version: %s\n", VERSION);
    printf("Max Users: %d\n", MAX_USERS);
    
    print_banner();
    int result = calculate_sum(10, 20);
    printf("Sum: %d\n", result);
    
    #if DEBUG_MODE
    log_message("Debug mode is active");
    #endif
    
    return 0;
}
Header Guard Best Practice:

Always use include guards (#ifndef/#define/#endif) in header files to prevent multiple inclusions and compilation errors.

#ifndef HEADER_NAME_H
#define HEADER_NAME_H
// Header contents here
#endif // HEADER_NAME_H

The #define Directive and Macros

The #define directive creates macros - symbolic names that represent values or code fragments. Macros are replaced by their definitions during preprocessing.

Syntax:
#define IDENTIFIER replacement_text // Object-like macro #define MACRO(parameters) replacement_text // Function-like macro

Macro Expansion Process:

How Macros Work:
Source Code
#define PI 3.14159
area = PI * r * r;
Preprocessor
After Expansion
area = 3.14159 * r * r;

Key Characteristics:

  • Text substitution: Simple replacement, no type checking
  • No memory allocation: Macros don't consume memory at runtime
  • Scope: From point of definition to end of file or #undef
  • Naming convention: Use uppercase for macros (common practice)

#define Directive Examples:

Example 1: Object-like Macros (Constants)
#include <stdio.h>

// Simple constants
#define PI 3.14159265359
#define MAX_SIZE 100
#define PROGRAM_NAME "My Application"
#define NEWLINE '\n'
#define TAB '\t'

// Expressions as macros
#define MINUTES_PER_HOUR 60
#define HOURS_PER_DAY 24
#define MINUTES_PER_DAY (MINUTES_PER_HOUR * HOURS_PER_DAY)

// Conditional values
#define DEBUG 1
#define VERSION 2

int main() {
    double radius = 5.0;
    double area = PI * radius * radius;
    double circumference = 2 * PI * radius;
    
    printf("Program: %s\n", PROGRAM_NAME);
    printf("Radius: %.2f\n", radius);
    printf("Area: %.2f\n", area);
    printf("Circumference: %.2f\n", circumference);
    
    printf("Minutes in a day: %d\n", MINUTES_PER_DAY);
    
    #if DEBUG
    printf("Debug information printed...\n");
    #endif
    
    return 0;
}
Example 2: Function-like Macros
#include <stdio.h>

// Simple 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))

// Multi-line macro (use backslash for continuation)
#define PRINT_ERROR(msg) \
    printf("ERROR: %s\n", msg); \
    printf("File: %s, Line: %d\n", __FILE__, __LINE__)

// Stringizing operator (#)
#define STRINGIZE(x) #x

// Token pasting operator (##)
#define CONCAT(a, b) a##b

int main() {
    int num1 = 10, num2 = 20;
    
    printf("Square of %d: %d\n", num1, SQUARE(num1));
    printf("Max of %d and %d: %d\n", num1, num2, MAX(num1, num2));
    printf("Absolute of -15: %d\n", ABS(-15));
    
    PRINT_ERROR("Division by zero");
    
    // Stringizing example
    printf("Stringized: %s\n", STRINGIZE(Hello World));
    
    // Token pasting example
    int value1 = 100, value2 = 200;
    int result = CONCAT(value, 1) + CONCAT(value, 2);
    printf("Concatenated result: %d\n", result);
    
    return 0;
}
Example 3: Common Pitfalls and Solutions
#include <stdio.h>

// DANGEROUS: Missing parentheses
#define SQUARE_BAD(x) x * x

// SAFE: Parentheses around parameters and whole expression
#define SQUARE_GOOD(x) ((x) * (x))

// DANGEROUS: Multiple evaluation
#define MAX_BAD(a, b) ((a) > (b) ? (a) : (b))

int main() {
    printf("=== MACRO PITFALLS DEMONSTRATION ===\n\n");
    
    // Problem 1: Operator precedence
    int x = 5;
    printf("With SQUARE_BAD: %d + 1 squared = ", x);
    printf("%d (WRONG!)\n", SQUARE_BAD(x + 1));  // Expands to: x + 1 * x + 1
    
    printf("With SQUARE_GOOD: %d + 1 squared = ", x);
    printf("%d (CORRECT)\n", SQUARE_GOOD(x + 1)); // Expands to: ((x + 1) * (x + 1))
    
    // Problem 2: Multiple evaluation
    int counter = 0;
    
    int a = 5;
    int b = MAX_BAD(++a, 10);  // Expands to: ((++a) > (10) ? (++a) : (10))
    printf("\nAfter MAX_BAD(++a, 10):\n");
    printf("a = %d (expected 6, got %d)\n", a, a);
    printf("b = %d\n", b);
    
    // Problem 3: Side effects in macros
    #define INCREMENT_BAD(x) x++
    #define INCREMENT_GOOD(x) ((x) + 1)
    
    int val = 5;
    int result = INCREMENT_BAD(val) * 2;
    printf("\nINCREMENT_BAD(val) * 2 = %d\n", result);
    
    return 0;
}
Macro Best Practices:
  1. Use parentheses: Always put parameters and entire expression in parentheses
  2. Avoid side effects: Don't use expressions with side effects as macro arguments
  3. Use uppercase names: Convention to distinguish macros from variables
  4. Prefer const over #define: Use const variables instead of macros when possible
  5. Keep macros simple: Complex logic should be in functions, not macros
  6. Use inline functions: For function-like macros, consider inline functions instead
  7. Document macros: Comment complex macros explaining their purpose and usage

Conditional Compilation Directives

Conditional compilation directives allow you to include or exclude code based on conditions that can be evaluated at compile time. This is essential for platform-specific code, debugging, and feature toggles.

Syntax:
#ifdef MACRO_NAME // code to include if MACRO_NAME is defined #endif #ifndef MACRO_NAME // code to include if MACRO_NAME is NOT defined #endif #if EXPRESSION // code to include if EXPRESSION is true (non-zero) #elif OTHER_EXPRESSION // alternative code #else // default code #endif

Conditional Compilation Flow:

Source Code
Preprocessor
Evaluate Conditions
True
Include Code
False
Exclude Code
Modified Source

Conditional Compilation Examples:

Example 1: Debugging and Logging
#include <stdio.h>

// Define DEBUG_LEVEL during compilation or here
// Compile with: gcc -DDEBUG_LEVEL=2 program.c
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL 0  // Default: no debugging
#endif

// Debug macros based on level
#if DEBUG_LEVEL >= 1
#define DEBUG_PRINT(msg) printf("[DEBUG] %s\n", msg)
#else
#define DEBUG_PRINT(msg)  // Empty macro - no code generated
#endif

#if DEBUG_LEVEL >= 2
#define DEBUG_PRINT_DETAILED(msg, value) \
    printf("[DEBUG] %s: %d\n", msg, value)
#else
#define DEBUG_PRINT_DETAILED(msg, value)
#endif

#if DEBUG_LEVEL >= 3
#define DEBUG_PRINT_VERBOSE(msg, file, line) \
    printf("[DEBUG] %s (File: %s, Line: %d)\n", msg, file, line)
#else
#define DEBUG_PRINT_VERBOSE(msg, file, line)
#endif

int factorial(int n) {
    DEBUG_PRINT("Entering factorial function");
    DEBUG_PRINT_DETAILED("Parameter n", n);
    
    if (n < 0) {
        DEBUG_PRINT_VERBOSE("Negative input", __FILE__, __LINE__);
        return -1;
    }
    
    int result = 1;
    for (int i = 1; i <= n; i++) {
        result *= i;
        DEBUG_PRINT_DETAILED("Loop iteration", i);
    }
    
    DEBUG_PRINT_DETAILED("Result", result);
    DEBUG_PRINT("Exiting factorial function");
    
    return result;
}

int main() {
    printf("Debug Level: %d\n", DEBUG_LEVEL);
    
    int num = 5;
    int fact = factorial(num);
    
    if (fact > 0) {
        printf("Factorial of %d is %d\n", num, fact);
    } else {
        printf("Invalid input!\n");
    }
    
    return 0;
}
Example 2: Platform-Specific Code
#include <stdio.h>

// Platform detection (usually defined by compiler)
// Common predefined macros:
// __linux__       - Linux systems
// _WIN32, _WIN64  - Windows systems
// __APPLE__       - macOS systems
// __unix__        - UNIX systems
// __x86_64__      - 64-bit x86 architecture
// __i386__        - 32-bit x86 architecture

int main() {
    printf("=== PLATFORM INFORMATION ===\n\n");
    
    // Operating System
    #ifdef __linux__
    printf("Operating System: Linux\n");
    #define PLATFORM_NAME "Linux"
    #elif defined(_WIN32) || defined(_WIN64)
    printf("Operating System: Windows\n");
    #define PLATFORM_NAME "Windows"
    #elif defined(__APPLE__)
    printf("Operating System: macOS\n");
    #define PLATFORM_NAME "macOS"
    #else
    printf("Operating System: Unknown\n");
    #define PLATFORM_NAME "Unknown"
    #endif
    
    // Architecture
    #ifdef __x86_64__
    printf("Architecture: 64-bit x86\n");
    #elif defined(__i386__)
    printf("Architecture: 32-bit x86\n");
    #elif defined(__arm__)
    printf("Architecture: ARM\n");
    #else
    printf("Architecture: Unknown\n");
    #endif
    
    // Compiler
    #ifdef __GNUC__
    printf("Compiler: GCC version %d.%d.%d\n", 
           __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
    #elif defined(_MSC_VER)
    printf("Compiler: Microsoft Visual C++\n");
    #else
    printf("Compiler: Unknown\n");
    #endif
    
    // Compilation date and time
    printf("Compiled on: %s at %s\n", __DATE__, __TIME__);
    
    // Platform-specific code
    printf("\n=== PLATFORM-SPECIFIC GREETING ===\n");
    
    #ifdef __linux__
    printf("Hello Linux user!\n");
    // Linux-specific code here
    #elif defined(_WIN32) || defined(_WIN64)
    printf("Hello Windows user!\n");
    // Windows-specific code here
    #elif defined(__APPLE__)
    printf("Hello macOS user!\n");
    // macOS-specific code here
    #else
    printf("Hello from an unknown platform!\n");
    #endif
    
    return 0;
}
Example 3: Feature Toggles and Version Control
#include <stdio.h>

// Feature toggles (can be defined during compilation)
// Compile with: gcc -DFEATURE_A -DFEATURE_B program.c

// Version information
#define SOFTWARE_VERSION_MAJOR 2
#define SOFTWARE_VERSION_MINOR 1
#define SOFTWARE_VERSION_PATCH 0

int main() {
    printf("Software Version: %d.%d.%d\n\n",
           SOFTWARE_VERSION_MAJOR,
           SOFTWARE_VERSION_MINOR,
           SOFTWARE_VERSION_PATCH);
    
    printf("=== ACTIVE FEATURES ===\n");
    
    // Check and enable features
    #ifdef FEATURE_A
    printf("✓ Feature A is ENABLED\n");
    // Feature A implementation
    printf("  Performing Feature A operations...\n");
    #else
    printf("✗ Feature A is DISABLED\n");
    #endif
    
    #ifdef FEATURE_B
    printf("✓ Feature B is ENABLED\n");
    // Feature B implementation
    printf("  Performing Feature B operations...\n");
    #else
    printf("✗ Feature B is DISABLED\n");
    #endif
    
    #ifdef FEATURE_C
    printf("✓ Feature C is ENABLED\n");
    printf("  Performing Feature C operations...\n");
    #else
    printf("✗ Feature C is DISABLED\n");
    #endif
    
    printf("\n=== BUILD CONFIGURATION ===\n");
    
    // Build type
    #if defined(DEBUG_BUILD)
    printf("Build Type: DEBUG\n");
    printf("  - Debug symbols included\n");
    printf("  - Optimizations disabled\n");
    #elif defined(RELEASE_BUILD)
    printf("Build Type: RELEASE\n");
    printf("  - Debug symbols stripped\n");
    printf("  - Optimizations enabled\n");
    #else
    printf("Build Type: DEFAULT\n");
    #endif
    
    // Conditional code based on version
    #if SOFTWARE_VERSION_MAJOR >= 2
    printf("\nVersion 2.0+ features available:\n");
    printf("  - New user interface\n");
    printf("  - Enhanced security\n");
    #if SOFTWARE_VERSION_MINOR >= 1
    printf("  - Performance improvements (v2.1+)\n");
    #endif
    #endif
    
    return 0;
}

Other Preprocessor Directives

Beyond the commonly used directives, C provides several other preprocessor directives for specialized tasks.

Directive Purpose Example
#undef Undefines a previously defined macro
#undef MACRO_NAME
#pragma Compiler-specific instructions (implementation-defined)
#pragma once
#pragma pack(1)
#error Generates a compilation error with message
#error "Unsupported platform"
#line Changes the current line number and filename
#line 100 "myfile.c"
defined() Operator to test if a macro is defined (used with #if)
#if defined(MACRO)
Other Directives Examples
#include <stdio.h>

// #undef example
#define TEMP_MACRO 100

int main() {
    printf("=== #undef DIRECTIVE ===\n");
    printf("TEMP_MACRO before #undef: %d\n", TEMP_MACRO);
    
    #undef TEMP_MACRO
    
    // This would cause an error if uncommented:
    // printf("TEMP_MACRO after #undef: %d\n", TEMP_MACRO);
    
    printf("\n=== #error DIRECTIVE ===\n");
    
    // Platform validation
    #if !defined(__linux__) && !defined(_WIN32) && !defined(__APPLE__)
    #error "Unsupported platform! This code requires Linux, Windows, or macOS."
    #endif
    
    // Version validation
    #ifndef REQUIRED_VERSION
    #define REQUIRED_VERSION 2
    #endif
    
    #if SOFTWARE_VERSION_MAJOR < REQUIRED_VERSION
    #error "This program requires version 2.0 or higher"
    #endif
    
    printf("\n=== #line DIRECTIVE ===\n");
    
    // Report original line numbers
    printf("Current line: %d\n", __LINE__);
    printf("Current file: %s\n", __FILE__);
    
    // Change line number and filename
    #line 1000 "custom_file.c"
    
    printf("New line: %d\n", __LINE__);
    printf("New file: %s\n", __FILE__);
    
    // Restore original
    #line __LINE__ "other-directives.c"
    
    printf("\n=== #pragma DIRECTIVE ===\n");
    
    // Common #pragma directives (compiler-specific)
    
    // GCC: Ignore specific warnings
    // #pragma GCC diagnostic ignored "-Wunused-variable"
    
    // Visual C++: Pack structure to 1-byte alignment
    // #pragma pack(push, 1)
    // struct PackedStruct {
    //     char a;
    //     int b;
    // };
    // #pragma pack(pop)
    
    // Once-only headers (non-standard but widely supported)
    // #pragma once
    
    printf("\n=== defined() OPERATOR ===\n");
    
    #if defined(DEBUG_MODE) && DEBUG_MODE == 1
    printf("Debug mode is active\n");
    #elif defined(DEBUG_MODE)
    printf("Debug mode is defined but not equal to 1\n");
    #else
    printf("Debug mode is not defined\n");
    #endif
    
    // Combined conditions
    #if defined(FEATURE_A) || defined(FEATURE_B)
    printf("Either Feature A or Feature B is enabled\n");
    #endif
    
    #if defined(FEATURE_A) && defined(FEATURE_B)
    printf("Both Feature A and Feature B are enabled\n");
    #endif
    
    return 0;
}

Predefined Macros

The C preprocessor provides several predefined macros that give information about the compilation environment.

Macro Description Example Value
__FILE__ Current source filename as a string literal
"program.c"
__LINE__ Current line number as a decimal constant
42
__DATE__ Compilation date as a string (Mmm dd yyyy)
"Jan 25 2024"
__TIME__ Compilation time as a string (hh:mm:ss)
"14:30:45"
__func__ Current function name as a string (C99)
"main"
__STDC__ Defined as 1 if compiler conforms to ISO C
1
__STDC_VERSION__ C standard version (long integer)
201112L (C11)
__cplusplus Defined for C++ compilation
(not defined in C)
Predefined Macros Example
#include <stdio.h>

void display_compilation_info() {
    printf("=== COMPILATION INFORMATION ===\n\n");
    
    printf("Source file: %s\n", __FILE__);
    printf("Current line: %d\n", __LINE__);
    printf("Compilation date: %s\n", __DATE__);
    printf("Compilation time: %s\n", __TIME__);
    printf("Current function: %s\n", __func__);
    
    #ifdef __STDC__
    printf("ISO C compliant: Yes (__STDC__ = %d)\n", __STDC__);
    #else
    printf("ISO C compliant: No\n");
    #endif
    
    #ifdef __STDC_VERSION__
    printf("C standard version: %ld\n", __STDC_VERSION__);
    
    // Interpret version
    #if __STDC_VERSION__ >= 201112L
    printf("  C11 or later\n");
    #elif __STDC_VERSION__ >= 199901L
    printf("  C99\n");
    #elif __STDC_VERSION__ >= 199409L
    printf("  C89 with Amendment 1\n");
    #else
    printf("  C89/C90\n");
    #endif
    #else
    printf("C standard version: Pre-C89\n");
    #endif
    
    #ifdef __cplusplus
    printf("Compiling as C++\n");
    #else
    printf("Compiling as C\n");
    #endif
}

// Custom assert macro using predefined macros
#define CUSTOM_ASSERT(condition) \
    do { \
        if (!(condition)) { \
            printf("Assertion failed: %s\n", #condition); \
            printf("File: %s, Line: %d\n", __FILE__, __LINE__); \
            printf("Function: %s\n", __func__); \
            return -1; \
        } \
    } while(0)

int process_data(int value) {
    CUSTOM_ASSERT(value >= 0);
    CUSTOM_ASSERT(value < 1000);
    
    printf("Processing value: %d\n", value);
    return value * 2;
}

int main() {
    display_compilation_info();
    
    printf("\n=== CUSTOM ASSERT DEMO ===\n");
    
    // This will succeed
    int result1 = process_data(50);
    printf("Result 1: %d\n", result1);
    
    // This will fail the assertion
    printf("\nTesting with invalid data:\n");
    int result2 = process_data(-5);  // This triggers assertion
    printf("Result 2: %d\n", result2);  // This line won't be reached
    
    return 0;
}

Common Mistakes and Best Practices

Common Mistake 1: Missing parentheses in macros
#define SQUARE_BAD(x) x * x int result = SQUARE_BAD(2 + 3); // Expands to: 2 + 3 * 2 + 3 = 11 (WRONG!)
#define SQUARE_GOOD(x) ((x) * (x)) int result = SQUARE_GOOD(2 + 3); // Expands to: ((2 + 3) * (2 + 3)) = 25 (CORRECT)
Common Mistake 2: Side effects in macro arguments
#define MAX_BAD(a, b) ((a) > (b) ? (a) : (b)) int x = 5; int y = MAX_BAD(++x, 10); // x incremented twice!
// Solution: Use functions or be careful with arguments inline int max_good(int a, int b) { return (a > b) ? a : b; }
Common Mistake 3: Forgetting header guards
// myheader.h WITHOUT guard struct Point { int x, y; }; // Including twice causes redefinition error
// myheader.h WITH guard #ifndef MYHEADER_H #define MYHEADER_H struct Point { int x, y; }; #endif // MYHEADER_H
Preprocessor Best Practices:
  1. Use const instead of #define for constants: const provides type safety
  2. Use inline functions instead of complex macros: Better debugging and type checking
  3. Always use header guards: Prevent multiple inclusion
  4. Parenthesize macro arguments: Avoid operator precedence issues
  5. Use uppercase for macro names: Distinguish from variables/functions
  6. Keep macros simple: Complex logic belongs in functions
  7. Document conditional compilation: Explain why code is included/excluded
  8. Use #pragma sparingly: Compiler-specific code reduces portability
  9. Test both sides of conditionals: Ensure code works with/without features
  10. Use #error for required configuration: Fail early with helpful messages
When to Use Macros vs Alternatives:
Use Case Macro Alternative Recommendation
Simple constants #define PI 3.14 const double PI = 3.14; Use const (type safety)
Inline code #define SQUARE(x) ((x)*(x)) inline int square(int x) { return x*x; } Use inline function
Debugging #ifdef DEBUG if (debug_mode) {} Use macros (compile-time)
Platform-specific code #ifdef _WIN32 Runtime detection Use macros (compile-time)

Key Takeaways

  • C preprocessor directives begin with # and are processed before compilation
  • #include inserts file contents (<> for system, "" for local files)
  • #define creates macros for constants and code substitution
  • Always use parentheses in macro definitions to avoid precedence issues
  • #ifdef/#ifndef check if a macro is defined/not defined
  • #if/#elif/#else/#endif provide conditional compilation
  • Use header guards (#ifndef/#define/#endif) to prevent multiple inclusions
  • Predefined macros like __FILE__, __LINE__, __DATE__ provide compilation info
  • #error generates compilation errors with custom messages
  • #pragma provides compiler-specific instructions (use sparingly)
  • Prefer const variables over #define for constants (type safety)
  • Prefer inline functions over complex macros (better debugging)
  • Use conditional compilation for platform-specific code and debugging
Next Topics: We'll explore structures and unions in detail, including declaration, initialization, access, and practical applications in data organization.