預處理(預編譯)是編譯器對源碼進行詞法掃描前的處理階段,處理以 # 開頭的指令。合理使用預處理能大幅提升代碼的可讀性、可移植性與可維護性。
#define MAX_TIME 1000
#define PI 3.1415926
注意事項:
const 更安全)#undef 終止作用域
#define SQUARE(x) ((x)*(x)) // 關鍵:參數與整體雙重加括號
陷阱示例:
#define SQ(r) r*r
int area = SQ(a+b); // 展開為 a+b*a+b 錯誤!
// 正確:#define SQ(r) ((r)*(r))
| 特性 | 宏 | 函數 |
|---|---|---|
| 處理階段 | 預處理(編譯前) | 編譯/鏈接/運行 |
| 類型檢查 | 無 | 有 |
| 參數求值 | 不求值,直接替換 | 先求值再傳遞 |
| 副作用 | SQUARE(i++) 導致 i 增加兩次 | Square(i++) 僅增加一次 |
| 代碼膨脹 | 是 | 否 |
| 調試可見性 | 不可見(展開後) | 可見 |
// 1. 最大/最小值
#define MAX(x,y) (((x)>(y))?(x):(y))
#define MIN(x,y) (((x)<(y))?(x):(y))
// 2. 陣列元素個數
#define ARR_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
// 3. 結構體成員偏移量
#define OFFSET(type, member) ((size_t)&(((type*)0)->member))
// 4. 容器宏(獲取結構體指針)
#define CONTAINER_OF(ptr, type, member) ((type*)((char*)(ptr) - OFFSET(type, member)))
// 5. 字符串化 #
#define STR(x) #x
#define TO_STR(x) STR(x) // 雙層展開讓參數先展開
// 6. 符號連接 ##
#define CONCAT(a,b) a##b
#define DECLARE_VAR(name) int CONCAT(var_, name)
// 7. 可變參數宏 (C99)
#define LOG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
// 8. 避免多語句宏陷阱
#define DO(a,b) do { a; b; } while(0)
// 9. 防止頭文件重複包含
#ifndef HEADER_H
#define HEADER_H
// 內容
#endif
#include <stdio.h> // 系統頭文件:搜索標準包含目錄
#include "myhead.h" // 用戶頭文件:先搜索當前目錄,再搜索標準目錄
#include 只能包含一個文件
// #ifdef / #ifndef
#ifdef DEBUG
printf("Debug: %d\n", x);
#endif
#ifndef CONFIG_H
#define CONFIG_H
// 配置
#endif
// #if / #elif / #else / #endif
#if VERSION >= 2
// 新版本代碼
#elif VERSION == 1
// 舊版本代碼
#else
#error "Unknown version"
#endif
#ifdef WINDOWS ... #else ... #endif#ifdef FEATURE_FLV ... #endif#ifdef DEBUG ... #endif,發布版刪除 #define DEBUG#ifndef TEST ... #else i=0; #endif.h 只 extern 聲明,.c 定義
__LINE__ // 當前行號
__FILE__ // 當前文件名
__DATE__ // 編譯日期 "Mmm dd yyyy"
__TIME__ // 編譯時間 "hh:mm:ss"
__STDC__ // 標準C編譯器為1
__func__ // 當前函數名 (C99)
#ifdef LOG_DEBUG
#define PRINT_DEC(x) printf(#x " = %d\n", x)
#define PRINT_HEX(x) printf(#x " = 0x%X\n", x)
#define TRACE(fmt, ...) do { \
printf("[%s(%d)<%s>] ", __FILE__, __LINE__, __func__); \
printf(fmt, ##__VA_ARGS__); \
} while(0)
#else
#define PRINT_DEC(x)
#define PRINT_HEX(x)
#define TRACE(fmt, ...)
#endif
const/inline/enum 替代宏定義常量/函數#、##、可變參數實現元編程能力預處理器是C語言獨特的元編程工具,用好它能寫出靈活高效的系統級代碼;濫用它則會製造難以維護的「宏地獄」。