⓪編著 : 蕭沖

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了。所以他必須使用如下:

extern "C"
{
#include "C_LIB.h"  //C_LIB 是C語言所製告出來的。
}

2/ 相反的,在C語言的編譯器裡若要使用由C++所製告出來的C函式庫,那麼也不能直接的使用C++的header檔。因為此header檔必然存在extern "C" 這個關鍵字組,而這字組C語言是不認識的。所以必需要把C++的header檔裡的extern "C" { } 移除後才可以讓C編譯器使用。

創作者介紹
創作者 aftcast 的頭像
aftcast

蕭沖的書房

aftcast 發表在 痞客邦 留言(3) 人氣()


留言列表 (3)

發表留言
  • hi
  • 注意事項

    一組多載函數中, 只能有一個函數被指明為 "extern C", 因為符號修飾後的關係, ex:
    void abc(int);
    "extern C" void abc(char);
    "extern C" void abc(float);

    使用 C 方式的修飾符號是 _函數名, 因此是 _abc, 所以當地 2 組指明 "extern C" 也是 _abc 那麼 2組 C 函數的符號就重複了.
    此檢查在函數的多載就可以, 但是同一個 scope 中函數沒有多載化就無法檢查出, 更確切的說, "extern "C" 不受 namespace
    or class 修飾的影響, 因此同一編譯單元中只能有一個同名函數為 "extern C", 否則會有錯誤 ,ex:

    namespace N
    {
    extern "C" void abc(){} // _abc

    }

    extern "C" void abc(){} // _abc

    編譯時產生相同符號 _abc 函數定義故錯誤, 由於 extern "C" 不受限於 namespace 的修飾因此將產生一個有趣現象也就是宣告和定義
    可以在不同的 namespace 出現. 語法層面上的檢查就如同一般, 而當語法檢查通過後產生符號時, entern "C" 的定義其符號不帶有
    namespace 的修飾; 同樣的, 呼叫者其符號也不帶有 namespace 的修飾, ex:

    namespace Foo
    {
    extern "C" void abc(){} //有定義, 生成符號時, 不受限於 namespace Foo 修飾故符號為 _abc
    }

    namespace Bar
    {
    extern "C" void abc(); //純宣告
    }

    int main()
    {
    Bar::abc(); //函數呼叫, 生成符號時, 不受限於 namespace Bar 修飾故符號為 _abc, 事實上鏈結到 ::Foo::abc()
    }

    還有下面如此鏈結錯誤的狀況:
    由於extern "C" 沒有多載功能 , 不論是 void abc() 或是 void abc(int a) 都將被以 _abc 來做為函式名稱 , 這是極度危險的.

    1.c
    extern "C" void abc();
    int main()
    {
    abc();
    }

    2.c
    #include<stdio.h>
    extern "C" void abc(int a)
    {
    printf("%d",a);
    }


    當 1.c 和 2.c link 時可以 , 因為 c 沒有將參數名稱當作函式修飾名稱的一部分 , 故也就沒有多載 , 將 "C" 拿掉 link 時就會錯誤 ,
    因為會以 c++ 的方式來編譯.
  • wefuntw
  • 讚! 大解惑!!!
  • Ben Chan
  • THX 大神!