程式扎記: [C++ 文章收集] C++中關於extern "C"的意義

標籤

2010年8月31日 星期二

[C++ 文章收集] C++中關於extern "C"的意義


轉載自 這裡
extern "C" 是C++特有的組合關鍵字,在C裡並沒有這個的組合,僅有extern這個關鍵字!
為什麼C++會需要這樣的關鍵字組呢? 原因是C++它有一個複載(overloading)的功能,也就是說同樣的函式名稱可以有多個定義只要參數簽名不同即可。比如說C++裡可以有以下的二個宣告 :
bar(int i, int j);
bar(double i, double j);

這二個函式都是同樣的名字叫foo,僅參數型式不同。然而在C語言裡是不被允許的! C++是如何處理這同名的函式呢? 其實他在編譯時會偷偷的把這二個函式名變成不同的名字,舉例來說bar(int i, int j)可能會被改成_bar_int_int(每種compiler產生不太一樣),而另一個則被改成_bar_double_double。這技術稱Mangling.

問題來了! 當我們希望C++不要偷換函式名時該怎麼辦? 於是就有了extern "C" 這個關鍵字組出現了。這個字組就是請C++不要自己又偷天換日,請它保留原名。所以當我們宣告一個函式如下時 :
extern "C" bar(int i, int j);

編譯器就不會把bar變成_bar_double_double. 實際使用的注意事項 :
1. 當C++使用C的函式庫(library)時,C++不能直接套用C的header檔。因為他會把header裡的宣告給mangleing了。所以他必須使用如下:
  1. extern "C"  
  2. {  
  3. #include "C_LIB.h"  //C_LIB 是C語言所製告出來的。  
  4. }  
2. 相反的,在C語言的編譯器裡若要使用由C++所製告出來的C函式庫,那麼也不能直接的使用C++的header檔。因為此header檔必然存在extern "C" 這個關鍵字組,而這字組C語言是不認識的。所以必需要把C++的header檔裡的extern "C" { } 移除後才可以讓C編譯器使用.

其它有可能因為 extern "C" 造成的問題, 整理如下 :
一組多載函數中, 只能有一個函數被指明為 "extern C", 因為符號修飾後的關係, ex:
  1. void abc(int);  
  2. "extern C" void abc(char);  
  3. "extern C" void abc(float);  
使用 C 方式的修飾符號是 _函數名, 因此是 _abc, 所以當地 2 組指明 "extern C" 也是 _abc 那麼 2組 C 函數的符號就重複了. 此檢查在函數的多載就可以, 但是同一個 scope 中函數沒有多載化就無法檢查出, 更確切的說, "extern "C" 不受 namespace or class 修飾的影響, 因此同一編譯單元中只能有一個同名函數為 "extern C", 否則會有錯誤 ,ex:
  1. namespace N  
  2. {  
  3. extern "C" void abc(){} // _abc  
  4.   
  5. }  
  6.   
  7. extern "C" void abc(){} // _abc  
編譯時產生相同符號 _abc 函數定義故錯誤, 由於 extern "C" 不受限於 namespace 的修飾因此將產生一個有趣現象也就是宣告和定義可以在不同的 namespace 出現. 語法層面上的檢查就如同一般, 而當語法檢查通過後產生符號時, entern "C" 的定義其符號不帶有namespace 的修飾; 同樣的, 呼叫者其符號也不帶有 namespace 的修飾, ex:
  1. namespace Foo  
  2. {  
  3. extern "C" void abc(){} //有定義, 生成符號時, 不受限於 namespace Foo 修飾故符號為 _abc  
  4. }  
  5.   
  6. namespace Bar  
  7. {  
  8. extern "C" void abc(); //純宣告   
  9. }  
  10.   
  11. int main()  
  12. {  
  13. Bar::abc(); //函數呼叫, 生成符號時, 不受限於 namespace Bar 修飾故符號為 _abc, 事實上鏈結到 ::Foo::abc()  
  14. }  
還有下面如此鏈結錯誤的狀況 :
由於extern "C" 沒有多載功能 , 不論是 void abc() 或是 void abc(int a) 都將被以 _abc 來做為函式名稱 , 這是極度危險的.
  1. 1.c  
  2. extern "C" void abc();  
  3. int main()  
  4. {  
  5. abc();  
  6. }  
  7.   
  8. 2.c  
  9. #include  
  10. extern "C" void abc(int a)  
  11. {  
  12. printf("%d",a);  
  13. }  
當 1.c 和 2.c link 時可以 , 因為 c 沒有將參數名稱當作函式修飾名稱的一部分 , 故也就沒有多載 , 將 "C" 拿掉 link 時就會錯誤 ,
因為會以 c++ 的方式來編譯.
This message was edited 1 time. Last update was at 01/09/2010 09:42:26

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!