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

系統環境為:

目的:搞清以下幾個問題
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========================================================================================================
