C 語言中巨集與函數均可實現代碼復用,但本質機制迥異。巨集在預處理階段進行文本替換,函數在編譯後透過呼叫棧執行。本文從五大維度系統對比,助你做正確選擇。
| 維度 | 巨集 | 函數 |
|---|---|---|
| 代碼長度 | 每次展開插入,代碼膨脹 | 僅一份定義,呼叫處僅指令跳轉 |
| 執行速度 | 無呼叫開銷,較快 | 需壓棧/彈棧、跳轉/返回,略慢 |
| 運算符優先級 | 易受上下文影響,必加括號 | 參數先求值再傳入,邏輯可預測 |
| 參數求值 | 每次展開重新計算,副作用多發 | 呼叫前求值一次,結果固定 |
| 參數類型 | 無類型檢查,僅文本替換 | 嚴格類型檢查,類型不匹配報錯 |
#define SQUARE(x) ((x)*(x))
inline int square_func(int x) { return x*x; }
巨集每處使用皆插入乘法表達式,函數僅保留一份機器碼。大量使用巨集會導致目標檔膨脹(代碼膨脹),影響指令快取命中率。
巨集無函數呼叫指令(call/ret)、無棧框架建立/銷毀,適合極簡運算(如 MAX、ABS)。但現代編譯器對 inline 函數積極內聯,實際性能差距常可忽略。
#define MUL(a,b) a*b // 危險!
#define MUL_SAFE(a,b) ((a)*(b)) // 安全
int x = MUL(2+3, 4); // 展開:2+3*4 = 14 ❌
int y = MUL_SAFE(2+3,4); // 展開:(2+3)*(4) = 20 ✅
鐵律:巨集參數與整體表達式必雙層括號包裹。
#define MAX(a,b) ((a)>(b)?(a):(b))
int i = 5;
int m = MAX(i++, 10); // i++ 被展開兩次,i 變 7 ❌
// 函數版:int max_func(int a,int b) { return a>b?a:b; }
// max_func(i++, 10); // i 僅增 1 ✅
巨集參數若含副作用(++、函數呼叫、賦值),會導致多次求值,邏輯錯誤極難排查。
MAX(3.14, 2.71)、MAX("abc", "def") 皆合法(若運算符支援),編譯器不檢查類型。int max(int,int) 傳入 double 會隱式轉換或報警,類型不匹配直接報錯。inline 函數 / constexpr (C++):類型安全、無副作用、編譯器可優化、偵錯可見。#define MAX_SIZE 1024)、條件編譯(#ifdef DEBUG)、型別無關的簡單運算(且參數無副作用)、編譯器不支援內聯的極老舊環境。static inline(C99)或 __forceinline(MSVC)。理解巨集「文本替換」本質,才能避開經典坑,寫出可維護的 C 代碼。