NDK開發(fā)中C與C++互相調(diào)用處理

?????? 在NDK開發(fā)中難免會遇到C與C++混合編程以故,比如C調(diào)用C++寫的so庫或者C++調(diào)用C的函數(shù)庫榔昔,如果不做特別處理收奔,就會出現(xiàn)編譯通過但鏈接時找不到函數(shù)或者壓根就編譯不通過的情況。

?????? 為什么會出現(xiàn)這種情況毫捣?

有兩個原因:

1.C++比C出現(xiàn)的晚详拙,在C++里出現(xiàn)了很多新的特性,比如類蔓同、命名空間等饶辙,是C不支持的,因為C不能向下兼容斑粱,比如在C程序中直接使用new關鍵字生成一個對象是不支持的弃揽。

2.C++支持函數(shù)重載,C不支持函數(shù)重載则北,在編譯后矿微,C++的函數(shù)名會被修改,而C的函數(shù)名基本上不變尚揣,由于兩者在編譯后函數(shù)名的命名策略不同涌矢,所以在不處理的情況下,C調(diào)用C++的函數(shù)或者C++調(diào)用C函數(shù)快骗,都會在鏈接階段報找不到函數(shù)的錯誤娜庇。

如何解決函數(shù)名找不到的問題

????????? 使用 extern "C" { }

????????? 作用:通知C++編譯器將extern? "C"所包含的代碼按照C的方式編譯和鏈接

?????????? 注意:extern "C" 是在C++里增加的,C里面不能識別

有兩種調(diào)用情況:

1.C++調(diào)用C函數(shù)

??? a.如果C函數(shù)在.h中聲明方篮,我們直接用extern "C"包含函數(shù)名秀,比如這樣:

/* file myCHead.h */
#ifndef MYCHEAD_H
#define MYCHEAD_H

extern "C" {

int funC();

}

#endif //MYCHEAD_H

那么C++調(diào)用該函數(shù)時,只需要引入myCHead.h即可:

/* file myCppSource.cpp */

#include "stdio.h"
#include "myCHead.h"

int main()
{
printf("funC:%d", funC());
return 0;
}

這時候cpp里include進來的myCHead.h恭取,因為funC()函數(shù)有 extern "C"包含泰偿,那么C++編譯器不會對funC()函數(shù)名采用C++策略處理,而采用C編譯器策略蜈垮,而.c的代碼NDK會默認采用C編譯器進行編譯,正因為函數(shù)的調(diào)用處與實現(xiàn)處都采用了相同的命名策略裕照,故最后能正確鏈接攒发。

接下來實現(xiàn).c的程序:

/* file myCSource.c */
#include "myCHead.h"

int funC()
{
return 100;
}

這時候myCSource.c里也include了myCHead.h,那么問題出現(xiàn)了晋南,如果我們把myCSource.c展開(因為 #include 的本質(zhì)就是把對應的文件直接拷貝到這一行里面 ):

/* file myCSource.c */
extern "C" {

int funC();

}

int funC()
{
return 100;
}

前面說過惠猿,extern "C"是C++里加入的,在C里面并不識別负间,所以這里在編譯時就會報錯

解決方案:使用 #ifdef?__cplusplus偶妖,因為“__cplusplus”是C++中的自定義宏,C里面沒有政溃,所以當.h可能被C與C++同時使用時趾访,又使用了extern "C",這時使用 #ifdef?__cplusplus 來規(guī)定董虱,如果是C的代碼扼鞋,就不需要使用extern "C"申鱼,所以正確的 myCHead.h為:

/* file myCHead.h */
#ifndef MYCHEAD_H
#define MYCHEAD_H

#ifdef __cplusplus
extern "C" {
#endif

int funC();

#ifdef __cplusplus
}
#endif

#endif //MYCHEAD_H

在這里要明白,#ifdef?__cplusplus主要為了防止extern "C"被C程序使用云头,假設頭文件只被C++使用捐友,那就不需要用 #ifdef?__cplusplus。

b.如果沒有 myCHead.h頭文件溃槐,如何做

?? 很簡單匣砖,前面說了 #include 的本質(zhì)就是把對應的文件直接拷貝到這一行里面,所以直接在C++程序中聲明一下 funC()函數(shù):

/* file myCppSource.cpp */

#include "stdio.h"
extern "C" int funC();

int main()
{
printf("funC:%d", funC());
return 0;
}

對編譯器來說昏滴,其實質(zhì)與引用頭文件一樣猴鲫,還省了#ifdef?__cplusplus

2.C調(diào)用C++程序

?????? C調(diào)用C++程序比C++調(diào)用C要復雜一些,因為除了同樣會有編譯后函數(shù)命令策略不同的問題影涉,還會有C不能向下兼容C++新特征的問題

a.C調(diào)用C++的全局函數(shù)(不在類里面)

?? 此時C能直接調(diào)用C++的函數(shù)变隔,但是如果不處理,同樣會在鏈接時報找不到函數(shù)名的錯誤蟹倾,所以還是要依賴extern "C"

??? 因為extern "C" 是給C++使用的匣缘,目的就是告訴C++編譯器,其函數(shù)命名規(guī)則采用C的方式鲜棠,這樣C調(diào)用了C++函數(shù)肌厨,在鏈接時就能找得到

??? 所以C++的頭文件可以這樣(比如C++中增加一個funCPP()函數(shù)提供給C用):

C++頭文件:

/* file myCPPHead.h */
#ifndef MYCPPHEAD_H
#define MYCPPHEAD_H

#ifdef __cplusplus
extern "C" {
#endif

int funCPP();

#ifdef __cplusplus
}
#endif

#endif //MYCPPHEAD_H

C++源程序:

/* file myCppSource.cpp */

#include "stdio.h"
#include "myCppHead.h"
extern "C" int funC();

int main()
{
printf("funC:%d", funC());
return 0;
}

int funCPP(){
return 200;
}

注意:#include "myCppHead.h"一定不能少,因為這句話就是告訴C++編譯器 函數(shù)funCPP()采用C的方式編譯豁陆,否則C程序調(diào)用funCPP()會出錯柑爸。

C增加調(diào)用C++函數(shù):

/* file myCSource.c */
#include "myCHead.h"
#include "myCppHead.h"

int funC()
{
return 100;
}

int main() {
printf("funCPP:%d", funCPP());
return 0;
}

正因為myCppHead.h被C引用了,所以myCppHead.h里的 extern "C"?盒音, 外面要用#ifdef?__clusplus包一下表鳍。

可以思考一下,如果C++沒有提供的.h祥诽,C如何使用其中的全局函數(shù)譬圣?

???? ?很顯然,可以在myCppSource.cpp源程序中雄坪,將提供給C調(diào)用的函數(shù)用??extern "C"進行修飾厘熟,C程序里用 extern int funCPP(); 聲明一下即可。

再思考一下维哈,如果C++里面有重載函數(shù)绳姨,如何被C調(diào)用?

比如 myCppSource.cpp變成:

/* file myCppSource.cpp */

#include "stdio.h"
#include "myCppSource.h"
extern "C" int funC();

int main()
{
printf("funC:%d", funC());
return 0;
}

int funCPP(){
return 200;
}

int funCPP(int value){
return value;
}

然后C需要調(diào)用funCPP(int value)怎么辦阔挠?

解決方法就是用另一個函數(shù)包裝一下飘庄,然后將該函數(shù)提供給C調(diào)用:

改造后的myCppHead.h:

/* file myCPPHead.h */
#ifndef MYCPPHEAD_H
#define MYCPPHEAD_H

int funCPP();
int funCPP(int value);

#ifdef __cplusplus
extern "C" {
#endif

int funCPP1();
int funCPP2(int value);

#ifdef __cplusplus
}
#endif

#endif //MYCPPHEAD_H

改造后的myCppSource.cpp:

/* file myCppSource.cpp */

#include "stdio.h"
#include "myCppHead.h"
extern "C" int funC();

int main()
{
printf("funC:%d", funC());
return 0;
}

int funCPP(){
return 200;
}

int funCPP(int value){
return value;
}

int funCPP1() {
return funCPP();
}

int funCPP2(int value) {
return funCPP(value);
}

C調(diào)用funCPP(int value),改造后的myCSource.c:

/* file myCSource.c */
#include "myCHead.h"
#include "myCppHead.h"

int funC()
{
return 100;
}

int main() {
//printf("funCPP:%d", funCPP());
printf("funCPP(int):%d", funCPP2(300));
return 0;
}


最后再思考一下谒亦,如果C++提供的庫竭宰,你沒辦法去修改頭文件與源文件空郊,向其中加上 extern "C",那么C如何調(diào)用其提供的函數(shù)切揭?

???? 也有解決辦法:也是包裝狞甚,可以增加一個新的cpp,里面使用包裝函數(shù)調(diào)用C++庫函數(shù)廓旬,然后將包裝函數(shù)用 extern "C"修飾后哼审,提供給C使用。

b.C調(diào)用C++的類函數(shù)

??????? 由于C不能向下兼容CPP的一些新特征孕豹,比如面向?qū)ο筇卣魃埽訡里不可能直接去new一個對象,再使用對象提供函數(shù)励背。我們前面有講使用包裝的方式間接調(diào)用重載函數(shù)的解決辦法春霍,正好在這里也可以用包裝的方式。

C++里增加一個類:

/* file myCPPHead.h */
#ifndef MYCPPHEAD_H
#define MYCPPHEAD_H

int funCPP();
int funCPP(int value);

class MyClass {
int funCPP();
};

#ifdef __cplusplus
extern "C" {
#endif

int funCPP1();
int funCPP2(int value);

#ifdef __cplusplus
}
#endif

#endif //MYCPPHEAD_H
/* file myCppSource.cpp */

#include "stdio.h"
#include "myCppHead.h"
extern "C" int funC();

int main()
{
printf("funC:%d", funC());
return 0;
}

int funCPP(){
return 200;
}

int funCPP(int value){
return value;
}

int funCPP1() {
return funCPP();
}

int funCPP2(int value) {
return funCPP(value);
}

int MyClass::funCPP(){
return 400;
}

請思考一下叶眉,上面這樣改造后址儒,可行否?

顯然是不行的衅疙, 因為?myCSource.c里有一句

#include "myCppHead.h"

而myCppHead.h里有class的聲明莲趣,編譯時myCSource.c肯定會報錯,如何解決饱溢?

兩個辦法喧伞,1、將MyClass的聲明放到.cpp中绩郎;2潘鲫、增加一個新的cpp來包裝

如果采用第一種方法,將MyClass的聲明放到.cpp中肋杖,再加上包裝函數(shù)次舌,則改造后的程序如下:

/* file myCPPHead.h */
#ifndef MYCPPHEAD_H
#define MYCPPHEAD_H

int funCPP();
int funCPP(int value);

#ifdef __cplusplus
extern "C" {
#endif

int funCPP1();
int funCPP2(int value);

//以下供C調(diào)用對象的成員函數(shù)
void* createObject();
int funCPP3(void* object);

#ifdef __cplusplus
}
#endif

#endif //MYCPPHEAD_H
/* file myCppSource.cpp */

#include "stdio.h"
#include "myCppHead.h"
extern "C" int funC();

class MyClass {
int funCPP();
};

int main()
{
printf("funC:%d", funC());
return 0;
}

int funCPP(){
return 200;
}

int funCPP(int value){
return value;
}

int funCPP1() {
return funCPP();
}

int funCPP2(int value) {
return funCPP(value);
}

void* createObject(){
return new MyClass();
}

int funCPP3(void* object){
MyClass* myClassObj = (MyClass*)object;

return myClassObj->funCPP();
}

int MyClass::funCPP(){
return 400;
}

myCSource.c里調(diào)用,改造后:

/* file myCSource.c */
#include "myCHead.h"
#include "myCppHead.h"

int funC()
{
return 100;
}

int main() {
//printf("funCPP:%d", funCPP());

//printf("funCPP(int):%d", funCPP2(300));

void *cppObj = createObject();
printf("class funCPP:%d", funCPP3(cppObj));

return 0;
}


如果不許直接修改myCppHead.h與myCppSource.cpp兽愤,向里面增加包裝函數(shù),則采用

另一種方式:增加一個.h與.cpp或者只增加.cpp來進行包裝:

myCppHead.h與myCppSource.cpp恢復成之前:

/* file myCPPHead.h */
#ifndef MYCPPHEAD_H
#define MYCPPHEAD_H

int funCPP();
int funCPP(int value);

#ifdef __cplusplus
extern "C" {
#endif

int funCPP1();
int funCPP2(int value);

#ifdef __cplusplus
}
#endif

#endif //MYCPPHEAD_H
/* file myCppSource.cpp */

#include "stdio.h"
#include "myCppHead.h"
extern "C" int funC();

class MyClass {
int funCPP();
};

int main()
{
printf("funC:%d", funC());
return 0;
}

int funCPP(){
return 200;
}

int funCPP(int value){
return value;
}

int funCPP1() {
return funCPP();
}

int funCPP2(int value) {
return funCPP(value);
}

int MyClass::funCPP(){
return 400;
}

新增的.h與.cpp:

/* file myWrapperCppHead.h */

#ifndef MYWRAPPERCPPHEAD_H
#define MYWRAPPERCPPHEAD_H

#ifdef __cplusplus
extern "C" {
#endif

void* createObjectFromWrapper();
int funCPP3FromWrapper(void* object);

#ifdef __cplusplus
}
#endif

#endif //MYWRAPPERCPPHEAD_H
/* file myWrapperCppSource.cpp */
#include "myWrapperCppHead.h"
#include "myCppHead.h"

void* createObjectFromWrapper(){
return new MyClass();
}

int funCPP3FromWrapper(void* object){
MyClass* myClassObj = (MyClass*)object;

return myClassObj->funCPP();
}

C調(diào)用成員函數(shù) 新的方式:

/* file myCSource.c */
#include "myCHead.h"
#include "myWrapperCppHead.h"

int funC()
{
return 100;
}

int main() {
//printf("funCPP:%d", funCPP());

//printf("funCPP(int):%d", funCPP2(300));

void *cppObj = createObjectFromWrapper();
printf("user wrapper class funCPP:%d", funCPP3FromWrapper(cppObj));

return 0;
}


最后總結(jié)一下:

1.C++中增加了extern "C" 關鍵字來告訴C++編譯器挪圾,函數(shù)命名采用C的方式

2.extern "C" 只能在C++中使用浅萧,了為防止頭文件中存在extern "C" ,而該頭文件可能會被C源程序include哲思,可以采用 #ifdef?__cplusplus 對 extern "C" 進行限制

3.C++能調(diào)用C函數(shù)的關鍵就是在C++里需要聲明C函數(shù)洼畅,且采用 extern "C" 修飾

4.C能調(diào)用C++非成員函數(shù)的關鍵就是 在C++中,C++函數(shù)需要使用 extern "C" 修飾

5.如果C++頭文件或源文件無法修改棚赔,使其加上 extern "C" 修飾時帝簇,可以增加一個新的包裝cpp徘郭,包裝cpp里采用包裝函數(shù)直接調(diào)用目標函數(shù),包裝函數(shù)使用 extern "C" 修飾后供C使用

6.C調(diào)用C++成員函數(shù)丧肴,需要使用包裝函數(shù)

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末残揉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子芋浮,更是在濱河造成了極大的恐慌抱环,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纸巷,死亡現(xiàn)場離奇詭異镇草,居然都是意外死亡,警方通過查閱死者的電腦和手機瘤旨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門梯啤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人存哲,你說我怎么就攤上這事因宇。” “怎么了宏胯?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵羽嫡,是天一觀的道長。 經(jīng)常有香客問我肩袍,道長杭棵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任氛赐,我火速辦了婚禮魂爪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艰管。我一直安慰自己滓侍,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布牲芋。 她就那樣靜靜地躺著撩笆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缸浦。 梳的紋絲不亂的頭發(fā)上夕冲,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音裂逐,去河邊找鬼歹鱼。 笑死,一個胖子當著我的面吹牛卜高,可吹牛的內(nèi)容都是我干的弥姻。 我是一名探鬼主播南片,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼庭敦!你這毒婦竟也來了疼进?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤螺捐,失蹤者是張志新(化名)和其女友劉穎颠悬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體定血,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡赔癌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了澜沟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灾票。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖茫虽,靈堂內(nèi)的尸體忽然破棺而出刊苍,到底是詐尸還是另有隱情,我是刑警寧澤濒析,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布正什,位于F島的核電站,受9級特大地震影響号杏,放射性物質(zhì)發(fā)生泄漏婴氮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一盾致、第九天 我趴在偏房一處隱蔽的房頂上張望主经。 院中可真熱鬧,春花似錦庭惜、人聲如沸罩驻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惠遏。三九已至,卻和暖如春骏啰,著一層夾襖步出監(jiān)牢的瞬間爽哎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工器一, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厨内。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓祈秕,卻偏偏與公主長得像渺贤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子请毛,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容