C/C++混合編程編譯問題


以下為本實驗使用的編譯器版本:

系統環境為:

 

目的:搞清以下幾個問題

1.  g++能否編譯c文件

2.  g++編出的s文件和gcc編出來的有何異同

3.  __cplusplus宏在何時被定義

4. c調用c++的注意事項

5. c++調用c的注意事項

6. 針對上述問題的makefile怎么寫比較好


 

問題1:g++能否編譯c文件

g++  -E  hello.c -o   g++_hello.i

gcc   -E  hello.c -o   gcc_hello.i

用beyond compare去對比一下二者的異同,可以看到

g++_hello.i關鍵是多了這個extern "C" 然后把stdio.h中的函數聲明和類型定義全部包住

“這個標識符的作用把標識符作用域的數據類型采用gcc去編譯”

【我感覺有點不對,這可能是stdio.h中自帶的#ifdef __cpluplus導致的,而不是預處理器搞的,我來看一下】

 上面是預處理的部分,下面看一下編譯結果的異同

gcc -S hello.c -o gcc_hello.s

g++ -S hello.c -o g++_hello.s

對比結果如下圖所示,顯然對於c文件的編譯,g++還是對函數名動了手腳

這里有個疑惑,為什么是.arch armv6,我的樹莓派明明是armv7的架構啊?

 

可以看到,生成的匯編指令中的函數名是不同的,生成的匯編指令也不同,這里和這篇文章說的就開始有出入了

后綴為.c的文件,gcc當做c程序去編,g++當做c++程序去編

https://blog.csdn.net/qq_21792169/article/details/85097822

gcc  -c hello.c -o gcc_hello.o

g++ -c hello.c -o g++_hello.o

g++編出來的明顯比較大

下面看一下生成的.o文件的反匯編結果是否相同呢

objdump -S gcc_hello.o > gcc_hello.obj

objdump -S g++_hello.o > g++_hello.obj

【可以看到也是有點差別的,不過反匯編后的文件大小竟然是一樣的,而.o文件大小不一樣,這tm是什么原因?】

 好的,繼續往下看,我們現在把hello.o 和main.o進行鏈接,其中main.c中調用了hello中的函數,看看gcc和g++去編譯鏈接會發生什么事情

gcc main.c hello.c -o gcc_main

g++ main.c hello.c -o g++_main

都沒有任何問題,編譯鏈接,包括運行都正常

分步看一下

gcc -S main.c -o gcc_main.s

g++ -S main.c -o g++_main.s

好的,沒有任何問題

現在的結論是,g++可以編譯c文件,編出來的結果是按照c++的方式去給函數命名,並且在main.c 中和hello.c中遵循同樣的symbol命名,所以編譯鏈接都是沒有問題的

下面開始看問題3,4,5

我現在main.c中想要調C++編寫的一個類的接口

Makefile為:

 先用gcc不鏈接stdc++(鏈接上stdc++也不行,不認識就是不認識)去make

【結果gcc壓根就不認識class等C++的關鍵字,說好的gcc能編呢?那用g++去編呢,沒有任何問題,完全通過】

結論:gcc編譯c文件,c文件中一定不能出現c++的語法和庫操作,如果有就用g++去編(g++把c文件當做c++去編)

現在再做一個實驗,把main.c改為main.cpp,然后用g++去編,嗯,測試下來沒問題,改用gcc去編

case 1: 不鏈接stdc++

case 2: 鏈接stdc++

嘿嘿,這里就有趣了,終於出現了我們想看到的狀況,main.cpp中鏈接不到hello函數的symbol,嘻嘻,hello.c是用gcc編譯的,你去看一下他的s文件,前面有

然后着重看一下main.cpp的s文件

gcc -lstdc++ -S main.cpp -o gcc_maincpp.s

我截取了一小部分

看到沒,這怎么可能鏈接到hello.o中的hello符號呢?

所以這里又可以得到幾個結論:

1、后綴.cpp的,gcc與g++都當成c++程序(gcc也可以編C++程序),gcc編的時候注意鏈接stdc++

2、C想調用C++,要么用g++去編譯C,要么把.C后綴改為.CPP

3、gcc不能自動鏈接c++庫,g++會自動鏈接c++庫

g++ 會自動進行 C++ 標准庫的連接;用 gcc 連接 C++ 程序也可以,但是需要人為指定連接 C++ 標准庫,否則就會出現undefined reference to `__gxx_personality_v/0' 之類的錯誤,gcc編譯 c++ 程序需要添加 -lstdc++   sample: gcc -lstdc++ -o test test.cpp

 OK,到這一步,我們應該知道了extern "C"的用處了,我們直接去hello.h中

然后重新make,咦?

 這里注意:如果使用gcc編譯,不使用__cplusplus宏,直接用extern “C”則會報錯。報錯為expected identifier or ‘(’ before string constant

而直接在main.cpp中像上圖那樣把#include "hello.h"括起來則是可以的

 原因是這樣的,extern "C" 放在hello.h頭文件中時,gcc以C程序的方式在處理hello.c時發現自己不認識extern "C",所以出了這個問題

C 語言中不支持extern "C" 聲明,在.c 文件中包含了extern "C" 時會出現編譯語法錯誤(error: expected identifier or ‘(’ before string constant )

而在main.cpp中,C++是認識這個的,所以可以直接用extern "C" {}包起來。

 在hello.h中改成我們最熟悉的這種形式,哈哈哈,make后就一切OK了

 

因此,我們又可以得到一些結論:

1、  在函數聲明前加上extern “C”,這是在提醒g++編譯鏈接這個函數時按照c函數機制去鏈接。同樣如果在程序中函數實體前加extern “C”,這是在提醒編譯器整個函數用gcc按照C規則去編譯。切記理解編譯與鏈接是兩碼事情

2、 在main.cpp中的#include "hello.h"上下加上extern "C" {}也是可以的,只不過這樣看着不美觀,因此,一般都在.h文件中通過#ifdef __cplusplus來加

關於__cpluplus宏何時會被定義的問題:

記住一個簡單的原則:只有當用gcc編譯.c程序時才會按照c規則編譯程序,其它情況下這個宏都是被定義的

這里引用一張圖,很容易說明這一點:

 

gcc test.c && ./a.out        @結果是:a+b=1314

g++ test.c && ./a.out        @結果是:a+b=520

gcc test.cpp && ./a.out      @結果是:a+b=520

g++ test.cpp && ./a.out      @結果是:a+b=520
---------------------
作者:HeroKern
來源:CSDN
原文:https://blog.csdn.net/qq_21792169/article/details/85097822
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

===================================================================

好了,上面同時回答了C調用C++的情況,以及CPP調用C的情況。

現在我們來簡單總結一下重要的結論:

1. C調C++的類或接口時,如果想用gcc編.C文件的話,C程序中一定不能出現C++的語法關鍵字和庫,否則根本過不了編譯(C不兼容C++),如果用g++編譯的話則OK,g++會把這個.C文件當做c++去編(因為C++是兼容C的,所以這樣是可行的)

2. C++調用C的接口時,.C可以用gcc去編,.C++既可以用g++去編,也可以用gcc去編(需要顯式鏈接-lstdc++)

3. 一般來說,.C的部分的頭文件.h中用#ifdef __cpluplus extern "C" {},可以使得C/C++混合編譯時很方便,main.cpp會在鏈接時,按照C的機制去鏈接

 

 最后說一說,C/C++混合編程時的makefile怎么寫比較好

以下內容引用自:https://blog.csdn.net/qq_21792169/article/details/85097822

1、如果是工程師自己寫編譯規則,那么在Makefile中應該檢測.c和.cpp文件分別用gcc與g++編譯,最后統一鏈接。

2、如果是IDE環境,由IDE環境決定,在qt中qmake是將.c文件用gcc編譯,g++編譯。

3、那么g++如何調用c程序封裝的函數或者庫呢,從上述講解知識可以看到c++程序直接調用c程序接口,這個肯定報錯提示找不到庫函數,c和c++處理函數接口機制不一樣,所以在c++程序中調用c程序應該在函數聲明前加上extern “C”,這是在提醒g++編譯鏈接這個函數時按照c函數機制去鏈接。同樣如果在程序中函數實體前加extern “C”,這是在提醒編譯器整個函數用gcc按照C規則去編譯。切記理解編譯與鏈接是兩碼事情。

【有讀者可能就問了,既然g++能夠編譯c程序,gcc還有必須要存在嗎?這個答案是肯定的。

原因一:操作系統全是按照c規則編寫與編譯(除開head.S等文件中的匯編),很多系統庫文件都是按照c規則寫與編譯的,

如果采用g++去編譯這一部分,那么生成的可執行文件會非常大,以及程序運行效率大大降低;還可能存在不能正常編譯通過情況,就是上述對比實驗中函數鏈接接口不一樣。

編譯器優化性能也是有限的,系統內核是整個上層應用的心臟,必須做到最高效率,不許存在過多冗余代碼(編譯器的問題)

原因二:做單片機出身的工程師對c語言扣字節操作應該是一種信仰,做上層應用的工程師更偏向c++面向對象,

一個大項目基本都是通力合作完成。所以gcc用來編譯c程序(效率問題,c實現對系統接口二次封裝),g++編譯c++程序。】 

 

 Done========================================================================================================


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM