1. 程式人生 > >C/C++函式相互呼叫

C/C++函式相互呼叫

注意這裡的C呼叫C++或者C++呼叫C意思是.c檔案中呼叫.cpp檔案中程式碼,或者相反。

整合開發環境如VC++6.0或者vs都是以檔案字尾來區別當前要編譯的是C程式碼還是C++程式碼,然後採用響應的編譯、呼叫協議等。

使用extern "C" 主要是因為C編譯器編譯函式時不帶引數的型別資訊,只包含函式的符號名字。如
int foo( float x )
C編譯器會將此函式編譯成類似_foo的符號,C聯結器只要找到了呼叫函式的符號,就認為連線成功。
而C++編譯器為了實現函式過載,會在編譯時帶上函式的引數資訊。如它可以把上面的函式編譯成類似於_foo_float這樣的符號。
所以,C呼叫C++,使用extern "C"則是告訴編譯器依照C的方式來編譯封裝介面,當然介面函式裡面的C++語法還是按C++方式編譯。


如:1 普通函式
// C++ Code
extern "C" int foo( int x );
int foo( int x )
{
   //...
}
這樣,編譯器會將foo函式編譯成類似_foo符號,而不會編譯成類似_foo_int符號
則C可以這樣呼叫C++函式
// C Code
int foo( int x );
void cc( int x )
{
    foo( x );
    //...
}
2 如果想呼叫過載的C++函式,則須封裝單獨的介面共C呼叫。

// C++ Code
void foo( int x );
void foo( float x );

extern "C" void foo_i( int x )
{
    foo( x );
}
extern "C" void foo_f( float x )
{
    foo( x );
}
則C中可這樣呼叫
// C Code
void foo_i( int x );
void foo_f( float x );
void ccc( int x1, float x2 )
{
    foo_i( x1 );
    foo_f( x2 );
    // ...
}
3 C中想呼叫C++中的成員函式(包括虛擬函式),則需要提供一個簡單的包裝(wrapper)。

例如: // C++ code:

class C

{

  ...

  virtual double f(int);

};

extern "C" double call_C_f(C* p, int i) // wrapper function

{

return p->f(i);

}

然後,你就可以這樣呼叫 C::f():

//C code

double call_C_f(struct C* p, int i);//宣告

void ccc(struct C* p, int i)

{

   double d=call_C_f(p,i);

 ...

}

問題:引數struct C* p從哪裡來,即怎麼在C中定義C++物件,其實上面只是說了思想,真實的c中使用C++類需要把原來的類都封裝一下,參看下面的文章


而C++呼叫C,extern "C" 的作用是:讓C++聯結器找呼叫函式的符號時採用C的方式 如:
// C Code
void foo( int x );

C++這樣呼叫C函式
// C++ Code
extern "C" void foo( int x );

就是讓C++聯結器能過類似於_foo來查詢此函式,而非類似於_foo_int這樣的符號。

時常在cpp的程式碼之中看到這樣的程式碼: 特別是C ++中引入C的標頭檔案,這些C標頭檔案中出現很多如下程式碼。

#ifdef __cplusplus extern "C" { #endif

//一段程式碼

#ifdef __cplusplus } #endif  

其中__cplusplus是C++編譯器的保留巨集定義.就是說C++編譯器認為這個巨集已經定義了. 所以關鍵是extern "C" {} extern "C"是告訴C++編譯器件括號裡的東東是按照C的obj檔案格式編譯的,要連線的話按照C的命名規則去找.

  要明白為何使用extern "C",還得從cpp中對函式的過載處理開始說起。在c++中,為了支援過載機制,在編譯生成的彙編碼中,要對函式的名字進行一些處理,加入比如函式的返回型別等等.而在C中,只是簡單的函式名字而已,不會加入其他的資訊.也就是說:C++和C對產生的函式名字的處理是不一樣的.

明白了加入與不加入extern "C"之後對函式名稱產生的影響,我們繼續我們的討論:為什麼需要使用extern "C"呢?C++之父在設計C++之時,考慮到當時已經存在了大量的C程式碼,為了支援原來的C程式碼和已經寫好C庫,需要在C++中儘可能的支援C,而extern "C"就是其中的一個策略。

  試想這樣的情況:一個庫檔案已經用C寫好了而且執行得很良好,這個時候我們需要使用這個庫檔案,但是我們需要使用C++來寫這個新的程式碼。如果這個程式碼使用的是C++的方式連結這個C庫檔案的話,那麼就會出現連結錯誤.

現在我們有了一個C庫檔案,它的標頭檔案是f.h,產生的lib檔案是f.lib,那麼我們如果要在C++中使用這個庫檔案,我們需要這樣寫:

extern "C" {

#include "f.h"

}