首先,我先了解一下編譯的過程。
分為四步:預處理(預處理用於將所有的#include頭文件以及宏定義替換成其真正的內容)——編譯(將經過預處理之後的程序轉換成特定彙編代碼(assembly code)的過程)——彙編(彙編過程將上一步的彙編代碼轉換成機器碼)——連結(連結過程將多個目標文以及所需的庫文件(.so等)連結成最終的可執行文件).
可以看下原文,大致了解一下:
https://www.cnblogs.com/carpenterlee/p/5994681.html
在這裡,就不過多分析編譯原理,把重心放在如何實現C與C++混合編程的問題上。
我們知道,C語言的發展快五十年了,它積累很多優秀的項目和庫,丟棄了很可惜,重寫也沒必要,那就研究如何在C++中調用它。
C++調用C語言的原理是什麼?
其實,C++的編譯和C語言是一樣的,都要經過四步。前四步是對文件單獨處理,而最後一步(連結)則是全員處理。
混合編程的」混合「操作發生在連結這一步。
c++連結時候,能夠找到對應的c語言函數的符號,那就意味著實現了混合編程。
先擺出結論:
C++編譯後,函數的符號會加上前後綴(與形參的類型有關),這樣做的好處是可以實現函數的多態(即根據你輸入的形參類型來調用相應的函數)。
而C語言編譯後,妥妥地一個沒有加工的函數名。
所以,要解決的問題是,如何讓C++成功連結上(或者對應上,又或者找得上)C語言的符號。
測試一:c++ 同名的函數編譯,然後反彙編查看符號表。
#include <iostream>
int add(int a, int b)
{
return (a + b);
}
double add(double a, double b)
{
return (a + b);
}測試二:gcc編譯C源碼
int add(int a, int b)
{
return (a + b);
}使用extern "C" {} ,讓C++兼容C語言。這個是C++特有的符號,為了讓編譯器遵循C語言規則。
測試:
extern "C"
{
int add(int a, int b)
{
return (a + b);
}
}編譯出.o文件:g++ test.cpp -c -o test.o
反彙編輸出:objdump test.o -d > test.i
結果:
用g++編譯輸出的符號與C語言一樣。
這樣,就解決了符號的問題。
實驗一:同一個項目全部有源碼,一次編譯連結
//test.cpp
#include <iostream>
#include "clib.h"
using namespace std;
int main()
{
int result;
result = add(1.1,2.0);
cout << "result: " << result << endl;
return 0;
}//clib.c
#include "clib.h"
int add(int a, int b)
{
return (a + b);
}//clib.h
#ifndef __MYLIB_H
#define __MYLIB_H
#ifdef __cplusplus //g++編譯器會定義這個宏,而gcc沒有
extern "C"
{
#endif
int add(int a, int b);
#ifdef __cplusplus
}
#endif
#endif實現同一個項目全部有源碼,一次編譯連結的方式是
g++ test.cpp clib.c clib.h -o test
這樣的編譯肯定沒問題,但存在一個大問題:c文件被當作c++源碼來編譯了(因為c++是c的超集),c語言原本的編譯高效就沒了,況且有時候項目的某些源碼就是用c語言來編寫,遵循C語言編譯規則比較好。
那麼怎麼處理?
用gcc單獨編譯 c文件,最後用g++全部連結在一起。
gcc clib.c -c -o clib.o
g++ test.cpp clib.o -o test
值得關注的一點是,在很多項目中,我們都能看到.h文件包含以下內容:
這樣做,是為了讓.h文件在g++編譯器中依舊按照C語言的規則來編譯,最終連結時候匹配到C庫。
實驗二: 同一個項目中C是庫,C++是源碼,C++調用C
生成一個靜態庫,這個庫是C語言編寫的。
ar r libclib.a clib.o
然後,我們用test.cpp去連結libclib.a庫。
g++ test.cpp -lclib -L. -o test
編譯成功。
成功的原因是,test.cpp裡包含的 clib.h文件中包含了 extern "C"。
在這裡,再引出一個問題,假如clib.h沒有包含 extern "C"呢?
也很好解決,在test.cpp裡補上。
#include <iostream>
extern "C"
{
#include "clib.h"
}
using namespace std;
int main()
{
int result;
result = add(1.1,2.0);
cout << "result: " << result << endl;
return 0;
}c++想要調用c源碼,但又是c源碼遵循c編譯器的規則,則要使用extern "C"。