# 淺談 `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))
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.