先說結論 : extern "C"只影響到鏈接期的name mangling
什么是name mangling?
請看 : C++函式多載的實作機制之name mangling - 知乎 (zhihu.com)
舉個例子 :
// external.h #ifdef __cplusplus extern "C" { #endif void external(); #ifdef __cplusplus } #endif
// external.cc #include "external.h" template <typename T> // 這明顯是C++特性, gcc是無法編譯的 void external() { T a; }
// main.c #include "external.h" int main() { external(); }
在這里 : main函式里面呼叫了c++函式external(), 如果這個函式沒被 extern "C"包裹, 會出現"undefined reference to"的錯誤.
這是因為g++、clang++都優先判斷后綴名 ( 除非自己改option ), 就會對external.cc檔案進行C++方式編譯 :
由于c++多載機制的存在, 編譯器只能在你命名之后 再悄悄重新給函式做個標記, 這個標記名也是有規則的, 比如g++, [ _Z + 函式名長度 + v / i ...標記符 ] .
external()函式被compiler賦予一個"符號", 如果是gcc, 這個符號就是external;
如果是g++, 這個符號是 _Z8externalv 或者_Z8externali;
而clang++更夸張, 變成了"?external@@YAHXZ"或者"?external@@YAHH@Z"這種怪物.
然后把obj檔案中定義和參考的global variable和function存到一張"符號表"中方便聯結器的作業.
------------------------------------------------------------------------------------------------------------------
名字 | 型別 | 是否可被外部參考 | 區域
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_Z8externalv | 定義, 參考 | 可 | 代碼段
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_Z8externali | 定義, 參考 | 可 | 代碼段
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main | 定義 | |
-------------------------------------------------------------------------------------------------------------------
聯結器說"我開始找了 ! ", 然后給main函式找external(), 發現沒有c版本的, 只有c++版本的呀, 它干不了了, "undefined reference to external".
如果你把宣告extern "C", 聯結器才能找到c版本的external(), 加上依賴庫/模塊 全打包起來. 還是拿gnu來說吧, c程式會打包libgcc.a靜態庫,c++程式則會打包很多libstdc++.a的東西和一些各式各樣的依賴, 這也是一般c++程式大小都要pure c大的原因, 在嵌入式平臺這種差距尤為明顯.
人家卡馬喬也有話說的 : " 我找的是什么表啊, 你這個表什么都有 我都能找到為什么不能鏈接? "
c檔案和c++檔案直接鏈接在一起, 聯結器應該用libgcc這個鏈子還是libstdc++呢?它沒法去判斷啊,只能你騙他一下這是c檔案, 用libgcc去鏈.
所以, 你extern "C"包裹函式的宣告, 并不會改變函式體內陳述句的實際編譯方式, 因為它的定義仍在.cc檔案里, g++仍會優先將它以c++的方式編譯. 但是函式名的確是以c的方式編去了.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/533477.html
標籤:C++
下一篇:c++基礎2
