原文地址:

https://www。

yanbinghu。com/2019/08/2

5/29412。html

前言

如何在C++程式碼中呼叫寫好的C介面?你可能會奇怪,C++不是相容C嗎?直接呼叫不就可以了?這裡我們先按下不表,先看看C++如何呼叫C程式碼介面。

C++如何呼叫C介面

為什麼會有這樣的情況呢?想象一下,有些介面是用C實現的,並提供了庫,那麼C++中該如何使用呢?我們先不做任何區別對待,看看普通情況下會發生什麼意想不到的事情。

首先提供一個C介面:

//來源:公眾號【程式設計珠璣】 部落格:https://www。yanbinghu。com

//test。c

#include

“test。h”

void

testCfun

()

{

printf

“I am c fun

\n

);

return

}

為了簡化,我們在這裡就不將它做成靜態庫或者動態庫了,有興趣的可以參考《靜態庫製作》自行嘗試。我們在這裡編譯成C目標檔案:

gcc -c test。c

另外提供一個頭檔案test。h:

#include

void

testCfun

();

我們的C++程式碼呼叫如下:

//來源:公眾號【

程式設計珠璣

】 部落格:https://www。yanbinghu。com

//main。cpp

#include

“test。h”

#include

using

namespace

std

int

main

void

{

/*呼叫C介面*/

cout

<<

“start to call c function”

<<

endl

testCfun

();

cout

<<

“end to call c function”

<<

endl

return

0

}

編譯:

$ g++ -o main main。cpp test。o

/tmp/ccmwVJqM。o: In function `main‘:

main。cpp:(。text+0x21): undefined reference to `testCfun()’

collect2: error: ld returned 1 exit status

很不幸,最後的連結報錯了,說找不到testCfun,但是我們確實定義了這個函式。為什麼會找不到呢?現在你還會認為C++直接就可以呼叫C介面了嗎?

真相

我們都知道,C++中函式支援過載,而C並不支援。C++為了支援函式過載,它在“生成”函式符號資訊時,不能僅僅透過函式名,因為過載函式的函式名都是一樣的,所以它還要根據入參,名稱空間等資訊來確定唯一的函式簽名。或者說

C++生成函式簽名的方式與C不一致

,所以即便是函式名一樣,對於C和C++來說,它們最終的函式簽名還是不一樣。當然這裡又是另外一回事了,我們不細說。我們看看兩個檔案裡的函式符號有什麼區別:

$ nm test。o|grep testCfun

0000000000000000 T testCfun

$ nm main。o|grep testCfun

U _Z8testCfunv

所以它們兩個能連結在一起才真是奇怪了呢!名字都不同,還怎麼連結?

如何處理

那麼如何處理呢?很顯然,我們必須告訴連結器,這是一個C介面,而不是C++介面,所以需要加入 extern C,我們修改test。h

#include

extern “C”{

void testCfun();

}

這裡用extern “C”將testCfun介面包裹起來,告訴編譯器,這裡的是C介面哈,你要按C程式碼的方式處理。再次編譯:

$ g++ -o main main。cpp test。o

$ 。/main

start to call c function

I am c fun

end to call c function

看終端輸出,完美!

最佳化

雖然上面的C介面可以被C++正常呼叫了,但是如果這個C介面要被C程式碼呼叫呢?增加main。c內容如下

//main。c

#include

“test。h”

int

main

void

{

/*呼叫C介面*/

testCfun

();

return

0

}

編譯:

$ gcc -o main main。c test。c

In file included from main。c:2:0:

test。h:2:8: error: expected identifier or ‘(’ before string constant

extern “C”{

^

In file included from test。c:2:0:

test。h:2:8: error: expected identifier or ‘(’ before string constant

extern “C”{

不出意外,又報錯了,很顯然,

C語言中並沒有extern "C"這樣的寫法

,所以為了能使得test。c的程式碼既能被C++呼叫,也能被C呼叫,需要改寫成下面這樣:

#include

#ifdef __cplusplus

extern

“C”

{

#endif

void

testCfun

();

#ifdef __cplusplus

}

#endif

這裡透過__cplusplus宏來控制是否需要extern “C”,如果是C++編譯器,那麼extern “C”部分就會被預處理進去,這樣test。c程式碼就可以既用於C++,也可以用於C啦。

趕快去你的C

專案程式碼頭

檔案中看看,是不是也有這樣的程式碼段呢?

https://www。

yanbinghu。com

問題

為什麼我們在C++程式碼中可以直接呼叫一些標準C庫函式呢?即使你在

main函式

中呼叫printf等函式,它也不會出現連結錯誤。因為庫函式已經有了類似的處理了。

如果你還是不確定,你可以先預處理:

$ g++ -E main。i main。cpp

去生成的main。i檔案中找一找,是不是有extern “C”。

總結

C++支援過載,而C不支援,C++並不能直接呼叫C程式碼寫好的介面,因此如果你的C程式碼想要能夠被C呼叫,也想被C++呼叫,那麼別忘了extern “C”。

原文地址:

https://www。

yanbinghu。com/2019/08/2

5/29412。html

推薦閱讀:

《C++函式過載》

《徹底理解C++過載函式匹配》

《靜態庫製作》

微信公眾號【程式設計珠璣】:專注但不限於分享計算機程式設計基礎,Linux,C語言,C++,

資料結構

與演算法,工具,資源等程式設計相關[原創]技術文章。