預處理器是 C 語言編譯流程的第一關,掌握條件編譯能讓你輕鬆實現跨平台、多配置、調試/發布雙模式構建。本文系統整理所有相關指令,並提供實戰模式與避坑指南。
| 指令 | 用途 | 關鍵特性 |
|---|---|---|
#define | 定義宏(物件/函數式) | 支援參數、可變參數 __VA_ARGS__ |
#undef | 取消宏定義 | 配合多次包含頭文件技巧 |
#if / #elif / #else / #endif | 常量表達式條件編譯 | 表達式可含宏、算術、邏輯運算 |
#ifdef / #ifndef | 檢測宏是否定義 | 單宏判斷,常用於防重複包含 |
defined | 在 #if/#elif 中檢測宏 | 可組合多條件:#if defined(A) && defined(B) |
#ifdef MACRO / #ifndef MACRO#if EXPR(非零為真)#if defined(A) && !defined(B) / #elif 鏈// foo.h
#ifndef FOO_H
#define FOO_H
// ... 宣告 ...
#endif // FOO_H
現代編譯器亦支援 #pragma once,更簡潔且避免宏名衝突。
// config.h
#define ENABLE_LOGGING 1
#define MAX_THREADS 64
// 使用處
#if ENABLE_LOGGING
#define LOG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define LOG(fmt, ...) ((void)0)
#endif
#ifdef _WIN32
#include
#define SLEEP(ms) Sleep(ms)
#elif defined(__linux__) || defined(__APPLE__)
#include
#define SLEEP(ms) usleep((ms)*1000)
#else
#error "Unsupported platform"
#endif
#define VERSION_MAJOR 2
#define VERSION_MINOR 5
#define VERSION_PATCH 1
#define VERSION_STRING "2.5.1"
#if VERSION_MAJOR > 2 || (VERSION_MAJOR == 2 && VERSION_MINOR >= 5)
#define HAS_NEW_API 1
#endif
// ops.def
OP(ADD, +)
OP(SUB, -)
OP(MUL, *)
// 產生 enum
#define OP(name, sym) name,
enum OpCode { #include "ops.def" };
#undef OP
// 產生字串表
#define OP(name, sym) #sym,
const char* op_str[] = { #include "ops.def" };
#undef OP
| 陷阱 | 說明 | 正確做法 |
|---|---|---|
#if MACRO 判斷宏是否定義 | 若 MACRO 定義為 0,#if 視為假 | 用 #ifdef 或 #if defined(MACRO) |
| 函數宏副作用 | MAX(i++, j) 導致 i 增加兩次 | 改用 inline 函數或 GNU 延伸 ({}) 語句表達式 |
| 宏參數未加括號 | SQR(x+1) 展開為 x+1*x+1 | 定義時全括號:#define SQR(x) ((x)*(x)) |
| 多行宏換行符遺漏 | 展開後語句貼合導致語法錯誤 | 每行結尾加 \,最後一行不加 |
#if 中使用未定義宏 | 視為 0,但會產生警告 | 先 #ifndef 判斷或用 defined() |
編譯器選項輸出預處理後代碼,驗證宏展開:
gcc -E source.c 或 gcc -E -dM source.c(顯示所有宏定義)cl /E source.c#define LOG(fmt, ...) printf(fmt, __VA_ARGS__)#define PRAGMA(x) _Pragma(#x) 允許在宏中生成 pragma#if __has_include() 檢測頭文件存在性條件編譯是 C 語言靈活性的核心來源。善用 #define/#if/defined 組合,配合 X-Macro、編譯期配置、平台隔離等模式,能構建清晰、可維護、跨平台的代碼庫。記住:宏是文本替換,非語義實體,嚴格加括號、避免副作用、善用編譯器輸出驗證,是高質量預處理代碼的鐵律。