預處理指令在編譯前執行,以 # 開頭,不屬於 C 語言語句範疇,不以分號結束。三大類:宏定義、條件編譯、文件包含。
#define MACRO_NAME replacement_text
#undef 終止k 開頭,與變量名區分#include <stdio.h>
#define COUNT 4
int main() {
char *name = "COUNT"; // 字符串內不替換
printf("%s\n", name); // 輸出 COUNT
int ages[COUNT] = {1, 2, 67, 89};
for (int i = 0; i < COUNT; i++) {
printf("%d\n", ages[i]);
}
#undef COUNT // 取消宏定義
// int a = COUNT; // 錯誤:COUNT 已失效
return 0;
}
#define SUM(v1, v2) ((v1) + (v2)) #define SQUARE(a) ((a) * (a))
黃金法則:參數與整體表達式都要加括號,防止運算符優先級導致錯誤。
| 特性 | 宏 | 函數 |
|---|---|---|
| 執行階段 | 預處理(文本替換) | 運行時(調用開銷) |
| 類型檢查 | 無 | 有 |
| 代碼膨脹 | 可能(每次調用展開) | 無(單一實體) |
| 調試可見性 | 差(無符號表) | 好 |
| 適用場景 | 簡單、高頻、類型無關操作 | 複雜邏輯、需類型安全 |
#define SQUARE(a) ((a)*(a))
int main() {
int c = SQUARE(5+5) / SQUARE(2);
// 展開:((5+5)*(5+5)) / ((2)*(2)) = 100 / 4 = 25 ✓
// 若無外層括號:#define SQUARE(a) a*a
// SQUARE(5+5) -> 5+5*5+5 = 35 ✗
// SQUARE(5+5)/SQUARE(2) -> 5+5*5+5/2*2 = 5+25+2.5*2 = 35 ✗
printf("c = %d\n", c); // 25
return 0;
}
#ifdef MACRO / #ifndef MACRO:宏是否定義#if constant_expr / #elif / #else / #endif:常量表達式判斷#error message:編譯報錯// myheader.h #ifndef MYHEADER_H #define MYHEADER_H // 內容... #endif
#ifdef _WIN32
#include
#elif defined(__linux__)
#include
#else
#error "Unsupported platform"
#endif
#include// 系統頭文件,搜尋標準路徑 #include "mydefs.h" // 用戶頭文件,優先搜尋當前目錄
包含文件內容會原位展開,等同於手動複製代碼。
編譯器內建,不可重定義:
__FILE__:當前源文件名__LINE__:當前行號__DATE__:編譯日期 "Mmm dd yyyy"__TIME__:編譯時間 "hh:mm:ss"__STDC__:是否符合 ANSI C 標準__cplusplus:C++ 編譯器標識#define LOG(fmt, ...) \
printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
int main() {
LOG("Value = %d", 42);
// 輸出:[main.c:10] Value = 42
return 0;
}
do { ... } while(0) 包裹成語句塊#define MUL(a,b) ((a)*(b))SQUARE(i++) 會導致 i 增加兩次inline 函數或 constexpr(C++)替代宏