1. Conditional Compilation Directives
Conditional compilation allows selective compilation of code based on different conditions, which is a key technology for achieving cross-platform compatibility, debugging switches, and version control.
1.1 <span>#ifdef</span> / <span>#ifndef</span>
Check if a macro is defined:
// Debug switch
#ifdef DEBUG
printf("Debug: x = %d\n", x);
#endif
// Prevent multiple inclusions
#ifndef CONFIG_H
#define CONFIG_H
// Header file content
#endif
Practical application example: Handling platform differences
#ifdef _WIN32
#include <windows.h>
#define SLEEP(ms) Sleep(ms)
#else
#include <unistd.h>
#define SLEEP(ms) usleep((ms) * 1000)
#endif
// Cross-platform usage
SLEEP(100); // Works on both Windows and Linux
1.2 <span>#if</span> / <span>#elif</span> / <span>#else</span>
Supports more complex conditional checks:
#if defined(__linux__)
#define PLATFORM "Linux"
#elif defined(_WIN32)
#define PLATFORM "Windows"
#elif defined(__APPLE__)
#define PLATFORM "macOS"
#else
#define PLATFORM "Unknown"
#endif
printf("Running on: %s\n", PLATFORM);
Expression evaluation:
#define VERSION 3
#if VERSION >= 3
void new_feature() {
printf("New feature available\n");
}
#else
void old_feature() {
printf("Using legacy version\n");
}
#endif
1.3 <span>defined</span> operator
Used to check if a macro is defined, often used with <span>#if</span>:
// Method 1: Using #ifdef
#ifdef DEBUG
// Method 2: Using defined (recommended, can combine complex conditions)
#if defined(DEBUG)
// Combined conditions
#if defined(DEBUG) && !defined(RELEASE)
printf("Debug mode (not release)\n");
#endif
#if defined(_WIN32) || defined(__linux__)
// Code for Windows or Linux platforms
#endif
1.4 Practical case: Feature switches
// config.h
#define FEATURE_LOGGING 1
#define FEATURE_ENCRYPTION 1
#define FEATURE_CACHE 0
// main.c
#if FEATURE_LOGGING
#define LOG(msg) printf("[LOG] %s\n", msg)
#else
#define LOG(msg) // No operation
#endif
#if FEATURE_ENCRYPTION
void encrypt_data(void* data, size_t len) {
// Encryption implementation
}
#else
void encrypt_data(void* data, size_t len) {
// No implementation or throw error
}
#endif
// Code usage remains unchanged, automatically selected at compile time
LOG("Application started");
encrypt_data(buffer, size);
2. Other Practical Directives
2.1 <span>#error</span> – Compilation error
Generates a compilation error actively, used for configuration checks:
#ifndef CONFIG_LOADED
#error "Configuration file not included!"
#endif
#if MAX_BUFFER_SIZE < 1024
#error "MAX_BUFFER_SIZE must be at least 1024"
#endif
// Check C standard version
#if __STDC_VERSION__ < 199901L
#error "This code requires C99 or later"
#endif
Application scenarios:
- Force configuration checks
- Unsupported platforms/compilers
- Dependency checks
2.2 <span>#warning</span> – Compilation warning
Generates a compilation warning (non-standard but supported by GCC/Clang):
#ifndef OPTIMIZED
#warning "Compiling without optimization, performance may be poor"
#endif
#if DEBUG
#warning "Debug mode enabled, not for production!"
#endif
2.3 <span>#line</span> – Line number control
Modifies the filename and line number reported by the compiler:
#line 100 "custom_file.c"
// Subsequent code errors will report from custom_file.c:100
// Actual application: Code generator
// generated_code.c (generated by tool)
#line 1 "original_template.tpl"
void generated_function() {
// Errors will point to the original template file
}
Uses:
- Code generation tools
- Debugging generated code
- Retaining original source file information
2.4 <span>#pragma</span> – Compiler directives
<span>#pragma</span> is used to pass special directives to the compiler, specific functionality depends on the compiler:
Common pragma directives:
// 1. Prevent multiple inclusions of header files
#pragma once
// 2. Structure memory alignment
#pragma pack(push, 1) // 1-byte alignment
struct PackedData {
char a;
int b;
};
#pragma pack(pop) // Restore default alignment
// 3. Disable specific warnings (GCC/Clang)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
int unused_var; // Will not generate a warning
#pragma GCC diagnostic pop
// 4. Disable specific warnings (MSVC)
#pragma warning(disable: 4996) // Disable warnings for unsafe functions
// 5. Code segment control
#pragma code_seg(".my_section")
void special_function() {
// Placed in a specific code segment
}
// 6. Optimization control
#pragma GCC optimize("O3")
void performance_critical_function() {
// High optimization level
}
Practical application: Embedded development
// Place data in Flash instead of RAM
#pragma DATA_SECTION(lookup_table, ".const")
const int lookup_table[256] = { /* ... */ };
// Interrupt service routine
#pragma CODE_STATE(ISR_Timer, 32)
#pragma INTERRUPT(ISR_Timer)
void ISR_Timer(void) {
// Interrupt handling
}
3. Predefined Macros
Standard macros built into the compiler, used to obtain compilation information:
3.1 Standard Predefined Macros
#include <stdio.h>
int main() {
printf("File: %s\n", __FILE__); // Current file name
printf("Line: %d\n", __LINE__); // Current line number
printf("Date: %s\n", __DATE__); // Compilation date (Mmm dd yyyy)
printf("Time: %s\n", __TIME__); // Compilation time (hh:mm:ss)
printf("Compiled: %s %s\n", __DATE__, __TIME__);
#ifdef __STDC__
printf("Standard C: %ld\n", __STDC_VERSION__);
#endif
return 0;
}
// Output example:
// File: main.c
// Line: 5
// Date: Nov 10 2025
// Time: 14:35:22
3.2 Function Name Macros
void my_function() {
printf("Function: %s\n", __func__); // C99: Function name
printf("Function: %s\n", __FUNCTION__); // GCC extension
}
// Output: Function: my_function
3.3 Utility Macros: Assertions and Logging
// Custom assertion
#define ASSERT(expr) \
if (!(expr)) { \
fprintf(stderr, "Assertion failed: %s, file %s, line %d\n", \
#expr, __FILE__, __LINE__); \
abort(); \
}
// Logging with location information
#define LOG_ERROR(fmt, ...) \
fprintf(stderr, "[ERROR] %s:%d:%s() " fmt "\n", \
__FILE__, __LINE__, __func__, ##__VA_ARGS__)
// Usage
ASSERT(ptr != NULL);
LOG_ERROR("Failed to open file: %s", filename);
// Output:
// [ERROR] main.c:45:process_file() Failed to open file: data.txt
3.4 Compiler-Specific Macros
// Detect compiler
#ifdef __GNUC__
printf("GCC version: %d.%d.%d\n",
__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#endif
#ifdef _MSC_VER
printf("MSVC version: %d\n", _MSC_VER);
#endif
#ifdef __clang__
printf("Clang version: %s\n", __clang_version__);
#endif
// Detect operating system
#ifdef __linux__
printf("Linux system\n");
#elif defined(_WIN32)
printf("Windows system\n");
#elif defined(__APPLE__)
printf("macOS system\n");
#endif
4. Comprehensive Practice: Building Information Module
// build_info.h
#pragma once
#define BUILD_DATE __DATE__
#define BUILD_TIME __TIME__
#define BUILD_FILE __FILE__
#ifdef DEBUG
#define BUILD_TYPE "Debug"
#else
#define BUILD_TYPE "Release"
#endif
#define PRINT_BUILD_INFO() \
printf("=== Build Information ===\n"); \
printf("Version: %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); \
printf("Build Type: %s\n", BUILD_TYPE); \
printf("Compiled: %s %s\n", BUILD_DATE, BUILD_TIME); \
printf("Compiler: "); \
PRINT_COMPILER_INFO(); \
printf("========================\n")
#define PRINT_COMPILER_INFO() \
do { \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdate-time\"") \
printf("GCC %d.%d.%d\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); \
_Pragma("GCC diagnostic pop") \
} while(0)
Summary
Conditional compilation and preprocessor directives are powerful tools in C language:
✅ Conditional Compilation: Achieve cross-platform compatibility, feature switches, version control✅ #error/#warning: Compile-time checks to detect issues early✅ #pragma: Fine control over compilation behavior✅ Predefined Macros: Obtain compilation information, assist debugging
The next article will introduceLinux kernel macro techniques and advanced macro programming techniques.