---
# System prepended metadata

title: 淺談 `C` 的 `inline`

---

# 淺談 `C` 的 `inline`
> 若您發現用詞不精確或語句不通順，歡迎直接修改。

## 研究動機
筆者同事有次問筆者為什麼以下程式會有連結時期的錯誤，出現 `fib.c:(.text+0xf): undefined reference to fib` 的錯誤訊息。為什麼在遞迴函式中加了 `inline` 會讓連結器找不到函式呢?
``` cpp
inline int fib(int n) {
    if (n < 2)
        return 1;
    return fib(n - 1) + fib(n - 2);
}

int main() {
    fib(10);
}
```
筆者希望可以藉著這個機會，閱讀C語言規格並記下歷程，以及試圖去理解規格所構思。

## 簡介
`inline` 修飾詞主要的功能是提醒編譯器這個函式值得被 `inline`，但僅是提醒而已，最後實作與否取決於編譯器的實作。

## 規格書裡面的 `inline`
問題主要來自於 [C99 規格書](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf)的原因是出自於 $6.7.4.6 的最後一行:
::: info 
For a function with external linkage, the following restrictions apply: (ignored ...)
It is unspecified whether a call to the function uses the inline definition or the external definition.
:::

在 `fib` 是 `external linkage` 的情況下，規格書並沒有規範 `main` 呼叫的 `fib` 是 `inline definition` 還是 `external definition`。而此案例中 `main` 並所 call 的 `fib` 是檔案外的 `fib`。

## 解決方案
第一個方案當然是不要在遞迴函數前加 `inline`，畢竟遞迴函數無法被 `inline`，有機會不會被編譯器選擇為 `callee`。

第一個方案其實有些狹隘，畢竟不是遞迴的函式也是有機會不被 `inline`。而這裡提供的第二個方案就是多寫一個實作在別的 `.c` 檔，使連結時期可以順利。

第三個方案是加上 `static`，強迫該函式轉為 `internal linkage`。因此即使編譯器判斷 `inline` 不划算，也不會選擇呼叫外部定義的函式。但值得注意的是，如果這個函式定義會在其他`translation unit` 使用的話， `internal linkage` 會讓其他 `translation unit` 無法使用。

第四個方案是加上 `extern`，令該函式轉為 `external definition`，因此該函式會在編譯結束後會被留下。但缺點是如果其他 `translation unit` 也有自己的實作且非 `inline` ，則連結時期會有重複定義的錯誤。

## 設計原由
同樣來自 $6.7.4.6，規格書解釋這樣的規範可以在同個 `translation unit` 中，提供實作的機會取代外在的函式定義。
::: info 
An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit.
:::

筆者認為這樣的設計有以下好處:
- 在不開啟 `LTO` 的情境，該 `trasnlation unit` 所呼叫的函式是外部的定義，在函式無法得知定義的情況，編譯器無法實作 `inline`。 因此我們可以利用 `inline` 提供實作。如果你願意相信編譯器 `inline` 的判斷，則在編譯器判斷不適合 `inline` 的場合，編譯器會放棄你提供的 `inline` 實作版本，選用呼叫外部定義的實作。
- 配合 `static` 加在 `inline` 上面可以放在 `.h` 檔，減少實作的程式碼。使用 `inline` 比起巨集，也有更好的型別檢查機制。(範例: [list.h](https://github.com/torvalds/linux/blob/master/include/linux/list.h))

