iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0

在第二天的文章中,我們提到巨集可以定義函數,但為何不用一般的函數宣告就好,還要用巨集定義函數?

使用巨集定義函數的好處

用巨集定義函數的好處是函數會直接在呼叫處展開執行,而不會出現一般函數的堆疊與參數傳遞,有助於提升效能,示意圖如下:
https://ithelp.ithome.com.tw/upload/images/20260804/20178513gxbhrF5dCJ.png
正常函數與用巨集定義函數的比較

透過上圖,我們可以看到若使用左邊的正常函數,則主函數 main() 呼叫 add() 函數時,會離開主函數,去 add() 函數處理當中的內容,這時主要會發生兩件耗效能的事情:

  1. main() 跳去執行 add() 函數又跳回 main()
  2. 參數 x = 3y = 5 要從 main() 傳到 add() 之後,再把運算結果傳回 main()

相較之下,若使用右邊的巨集,則巨集裡定義的函數在預處理階段就已經展開,就好像是一般在主函數中的運算式,不會有跳來跳去與參數傳遞的額外成本。

內嵌函數 Inline Function

在 C99 之後,C 語言另外推出「內嵌函數(inline function)」,其語法以 inline 開頭,例如:

#include <stdio.h>

inline int add_inline(int a, int b) {
    return a + b;
}

int main() {
    int x = 3, y = 5;
    int result = add_inline(x, y);
    printf("result = %d\n", result);
    return 0;
}

當中的 add_inline 函數會直接在 main() 函數裡面展開,運作流程與巨集相似:
https://ithelp.ithome.com.tw/upload/images/20260804/20178513t98gmki7Bf.png

既然巨集跟內嵌函數都能讓函數直接展開、那為何還要區分這兩者呢?

巨集 內嵌函數
無型別檢查 有型別檢查
參數可被計算超過一次 參數只算一次
較難除錯 較易除錯
不可遞迴 可遞迴

第四點是說內嵌函數可以自我呼叫、但巨集不行,比較好理解,前三點倒是可以透過一個實際範例說明。

第二天的文章中,我們提到如下程式碼可能產生副作用:

#include <stdio.h>

#define MIN(a, b) ((a) < (b) ? (a) : (b)) // 使用巨集無型別檢查

int main() {
    int arr[] = {5, 8, 12};
    int *p = arr;
    int b = 10;
    int least;

    least = MIN(*p++, b);

    printf("least = %d\n", least);
    printf("p points to = %d\n", *p);

    return 0;
}

這個 *p++ 會被執行兩次:

  1. 比較 *p++b 兩者大小,而 (*p++) 代表 arr[1],其值為是 8,因此 (*p++) < (b) 成立。
  2. 執行三元運算式問號右邊的 (a),而 (a)*p++,因此還最多計算一次 *p++,跑到 arr[2]

*p++ 在第二步多執行一次的結果導致最終 p 指標指向 arr[2]、而非上一步跟 b 比較大小時使用的 arr[1],形成副作用。

而若使用內嵌函數:

#include <stdio.h>

inline int min(int a, int b) { // 使用內嵌函數有型別檢查
    return a < b ? a : b;
}

int main() {
    int arr[] = {5, 8, 12};
    int *p = arr;
    int b = 10;
    int least;

    least = min(*p++, b);

    printf("least = %d\n", least);
    printf("p points to = %d\n", *p);

    return 0;
}

在進行 least = min(*p++, b); 時,*p++b 都已經先求好其值分別為 810 ,才接著進行 min() 運算,所以三元運算式中問號右方的 a 已經是 8、而非 *p++,因此不會多算一次。

內嵌函數的型別檢查與避免副作用的機制,讓我們得以比巨集更容易除錯,因此,除非要定義的函數非常簡單、或者使用 C99 以後版本的 C 語言,建議優先使用內嵌函數。

參考資料

  1. 【C++ 筆記】內嵌函數(inline function) - part 20
  2. Inline Function in C (GeeksforGeeks)
  3. Difference between Inline and Macro in C++ (GeeksforGeeks)

上一篇
Day 3 - 為何用 typedef 比 #define 定義新資料型別更好?
下一篇
Day 5-靜態變數與靜態函數
系列文
韌體工程師的不只 0x10 個問題5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言