C++是如何呼叫C介面的?
原文地址:
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++,
資料結構
與演算法,工具,資源等程式設計相關[原創]技術文章。