淺談C/C++混合編程

****如只想知道怎樣就能實(shí)現(xiàn)C/C++混合編程而不深究為什么的話, 可以一拉到底直接看總結(jié).****

首先, 在介紹C/C++混合編程之前, 先思考幾個(gè)問(wèn)題

1. C/C++混合編程是什么?

2. C/C++混合編程有什么用?

3. C/C++混合編程應(yīng)該怎么實(shí)現(xiàn)?

下面, 簡(jiǎn)單講講我對(duì)C/C++混合編程的理解 :?


1. C/C++混合編程是什么?

就像問(wèn)題本身所說(shuō), C/C++混合編程也就是一個(gè)工程中, 在C函數(shù)中調(diào)用C++函數(shù)的方法, 在C++的函數(shù)中能夠調(diào)用C函數(shù)的方法.


2. C/C++混合編程有什么用?

在我們?nèi)粘i_(kāi)發(fā)中, 也許會(huì)遇到這么一些情況, 同事A, C非常牛逼, 但是對(duì)C++一竅不通; 同事B, C++信手拈來(lái), 但是對(duì)C卻滿頭霧水. 但是在工作中有這么一種需求, 同事A需要用到C++的方法, 同事B需要用到C的方法,? 這怎么辦??

沒(méi)錯(cuò), 最簡(jiǎn)單的就是, 同事A把C的代碼寫(xiě)好, 然后同事B只管調(diào)用即可, 同理, 同事A只管調(diào)用同事B寫(xiě)好的C++代碼, 各司其職, 提高工作效率.?


3. C/C++混合編程應(yīng)該怎么實(shí)現(xiàn)?

那么, 這混合編程究竟要怎么實(shí)現(xiàn)呢?

在介紹之前, 我們先簡(jiǎn)單了解下以下幾個(gè)概念

1. 函數(shù)重載

2. C++的名字改編機(jī)制

3. extern 及 extern "C"

* 函數(shù)重載(Overloading)

C++和Java中的函數(shù)重載的定義一致,?

即在相同的作用域內(nèi), C++允許多個(gè)函數(shù)名稱相同, 而形參列表不同, 如下圖所示 :?

函數(shù)重載

然而大家有沒(méi)有想過(guò)為什么C++支持函數(shù)重載, 而C卻不支持函數(shù)重載呢??

這個(gè)就要涉及到C++的名字改編機(jī)制了. 請(qǐng)往下看~


* C++的名字改編機(jī)制

在C中,?

void test(); // 該函數(shù)編譯后編譯器會(huì)對(duì)函數(shù)名稱改寫(xiě)成 _test?


C語(yǔ)言中的test()函數(shù)


C語(yǔ)言編譯器改名后的test()函數(shù)叫_test

ps: 不提供test()函數(shù)的實(shí)現(xiàn)是讓Xcode鏈接的時(shí)候報(bào)錯(cuò), 這樣我們才能看清楚test()函數(shù)的真面目!

void test(int a); // 該函數(shù)編譯后編譯器改寫(xiě)函數(shù)名后依然是 _test


C語(yǔ)言中的test(int)函數(shù)


C語(yǔ)言編譯器改名后的test(int)函數(shù)依舊叫_test

在C++中,?

void test(); // 該函數(shù)編譯后編譯器會(huì)對(duì)函數(shù)名稱改寫(xiě)成 test()


C++編譯器改名后的test()函數(shù)叫test()

void test(int a); // 該函數(shù)編譯后編譯器改寫(xiě)函數(shù)名后是 test(int)


C++編譯器改名后的test(int)函數(shù)叫test(int)

ps : 有的系統(tǒng)的編譯器會(huì)編譯成 _test_int 這種格式, 名字改編機(jī)制只是一種思路, 并沒(méi)有一種唯一的命名規(guī)范, 不同的編譯器命名規(guī)范不同, 但是思路一致! 如下圖所示 :?


編譯器不同所產(chǎn)生的函數(shù)名可能不同

本文就舉這幾個(gè)例子, 大家可以自行嘗試重載多幾個(gè)函數(shù), 然后動(dòng)手試試看結(jié)果. 實(shí)踐才是王道!

通過(guò)上面幾個(gè)例子, 相信大家很容易就能知道為什么C++支持重載而C不支持重載了.

因?yàn)镃++有名字改編機(jī)制而C沒(méi)有!

所以在C中, 只要函數(shù)名相同, 不管你的形參列表如何南轅北轍, 編譯器均會(huì)將其編譯為同一函數(shù)名, 這樣在程序執(zhí)行過(guò)程中就會(huì)造成函數(shù)調(diào)用的二義性(也就是對(duì)于相同函數(shù)名的函數(shù), 程序并不知道應(yīng)該調(diào)用哪一個(gè)函數(shù)), 這是不允許的, 所以會(huì)報(bào)錯(cuò).?

然而對(duì)于C++而言, 盡管他們的函數(shù)名相同, 但是因?yàn)樗麄兊男螀⒘斜聿煌? 編譯器編譯后實(shí)際上會(huì)為他們改名為不同名字的函數(shù), 所以程序執(zhí)行調(diào)用函數(shù)的時(shí)候并不會(huì)產(chǎn)生二義性, 因此C++允許函數(shù)重載.?

這里扯一句題外話, C++的重載被認(rèn)為不是多態(tài), 因?yàn)槎鄳B(tài)是動(dòng)態(tài)運(yùn)行時(shí)對(duì)方法的綁定, 而C++的函數(shù)重載最多算是編譯時(shí)的"多態(tài)". (這句話不一定正確, 請(qǐng)大家糾正)


*?extern 及 extern "C"

extern相信大家比較熟悉, 它一般用來(lái)聲明一個(gè)函數(shù), 全局變量的作用域. extern告訴編譯器, 其聲明的函數(shù)和變量可以供本文件或者其他文件使用. 這里不再贅述.?

extern "C" 中的C是什么意思呢?

這里的C不是指C語(yǔ)言這一門(mén)語(yǔ)言, 而是表示一種編譯和鏈接的規(guī)約. C表示符合C語(yǔ)言的編譯和連接規(guī)約的任何語(yǔ)言,如Fortran(公式翻譯)、assembler(匯編語(yǔ)言)等。

注意 :?

extern "C" 只是指定編譯和鏈接的規(guī)約, 并不會(huì)影響語(yǔ)義, 所以在C++文件中該怎么寫(xiě)還得怎么寫(xiě), 必須遵循C++的語(yǔ)法規(guī)范.?

在C++源文件的語(yǔ)句前加上 extern "C" 的作用就是告訴編譯器, 這一段代碼按照類C的編譯和鏈接規(guī)約來(lái)編譯和鏈接(對(duì), 也就是按照類C的函數(shù)命名規(guī)范編譯)

小技巧 : 如果有多條語(yǔ)句需要extern "C", 可以用{ } 括住, 例如 :

extern "C" 用法

那應(yīng)該怎樣使用extern "C" 來(lái) 實(shí)現(xiàn)C/C++混合編程呢?

1. C中調(diào)用C++的代碼

2. C++中調(diào)用C的代碼

3. C/C++互調(diào)


* C中調(diào)用C++的代碼

廢話不多說(shuō), 上代碼.?


C調(diào)用C++中定義的sum(int, int)函數(shù)

毫無(wú)疑問(wèn), 這段代碼是鏈接不通過(guò)的, 為什么呢?

在C中, 編譯器會(huì)將main函數(shù)中調(diào)用的sum函數(shù)編譯為_(kāi)sum, 然而遠(yuǎn)在那邊寫(xiě)在C++文件中的sum函數(shù)則被編譯為sum(int, int), 則鏈接的時(shí)候編譯器會(huì)報(bào)找不到_sum函數(shù)的錯(cuò)誤. 如下圖


未定義的_sum函數(shù)

解決思路很明確, 只要確保函數(shù)在C和C++文件中編譯鏈接的規(guī)約一樣就OK了!

這時(shí)候extern "C" 就要閃亮登場(chǎng)了!

此時(shí)我們只需要在cpp文件中用extern "C" { } 把需要被C文件調(diào)用的函數(shù)包含即可, 如下圖

編譯成功, 輸出結(jié)果正確

* C++中調(diào)用C的代碼


C++調(diào)用C的sum(int, int)函數(shù)
同樣, 因?yàn)榫幾g鏈接函數(shù)命名規(guī)范不同導(dǎo)致找不到函數(shù)

此時(shí), 我們不能像之前C調(diào)用C++的方法來(lái)解決問(wèn)題, 原因是 extern "C" 并不能在C文件中使用, 況且C文件本來(lái)就遵循C的編譯和鏈接規(guī)約, 就算能在.c文件中使用extern "C" 也無(wú)濟(jì)于事.?

此時(shí)就涉及到#include的作用了, 眾所周知, #include相當(dāng)于文本拷貝, 等于把在頭文件中聲明的C函數(shù)原封不動(dòng)cpy到#include "zs.h"中, 如下圖

#include

有的朋友可能就想到了, 既然函數(shù)聲明已經(jīng)被copy到了C++文件中, 只需要保證C++文件中的這段函數(shù)聲明代碼按照類C的編譯, 鏈接規(guī)約進(jìn)行編譯和鏈接就能保證編譯出的sum函數(shù)的名稱與C文件中的函數(shù)名稱保持一致了!

所以便有了以下方法

Done!

細(xì)心的朋友就會(huì)發(fā)現(xiàn), 既然#include相當(dāng)于文本拷貝, 那為何不把extern "C" 語(yǔ)句放到頭文件中呢?

好的, 我們?cè)囋噡

果不其然, 運(yùn)行成功

但是我很遺憾的告訴大家, 如果你這樣做的話, 那么C文件就不能調(diào)用C文件的方法了!

因?yàn)镃的編譯器不支持 extern "C" 語(yǔ)法!

這里要引出一個(gè)宏, __cplusplus, 只要是C++文件, 編譯器就會(huì)自動(dòng)定義一個(gè)這樣的宏, 我們就能利用這個(gè)宏做到C/C++的終極混編了!

C調(diào)用C函數(shù)成功
C++調(diào)用C函數(shù)成功

總結(jié)

要想寫(xiě)一套C/C++均能調(diào)用的函數(shù), 則必須按照C的方式編譯 (因?yàn)镃語(yǔ)言不支持C++, 而C++同時(shí)支持C/C++)

要實(shí)現(xiàn)C/C++混合編程其實(shí)很簡(jiǎn)單, 只需要在頭文件加幾行代碼即可, 如下圖

C/C++混合編程核心代碼

有趣的是, Objective-C的函數(shù)編譯命名規(guī)范與C語(yǔ)言一樣, 由此可知如果要實(shí)現(xiàn)C/OC/C++混合編程, 跟C/C++編程是大同小異.


人生第一篇文章, 如果您耐心看完了, 非常感謝您的支持! 如有不對(duì)的地方, 非常歡迎大家指出錯(cuò)誤.


歡迎大家關(guān)注@Jerry4me, 關(guān)注菜鳥(niǎo)成長(zhǎng)^_^. 我會(huì)不定時(shí)更新一些學(xué)習(xí)心得與文章.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容