01
C与C++混合编程的原理
首先,我先了解一下编译的过程。
分为四步:预处理(预处理用于将所有的#include头文件以及宏定义替换成其真正的内容)——编译(将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程)——汇编(汇编过程将上一步的汇编代码转换成机器码)——链接(链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件).
可以看下原文,大致了解一下:
https://www.cnblogs.com/carpenterlee/p/5994681.html
在这里,就不过多分析编译原理,把重心放在如何实现C与C++混合编程的问题上。
我们知道,C语言的发展快五十年了,它积累很多优秀的项目和库,丢弃了很可惜,重写也没必要,那就研究如何在C++中调用它。
C++调用C语言的原理是什么?
其实,C++的编译和C语言是一样的,都要经过四步。前四步是对文件单独处理,而最后一步(链接)则是全员处理。
混合编程的”混合“操作发生在链接这一步。
c++链接时候,能够找到对应的c语言函数的符号,那就意味着实现了混合编程。
02
存在的难点
先摆出结论:
C++编译后,函数的符号会加上前后缀(与形参的类型有关),这样做的好处是可以实现函数的多态(即根据你输入的形参类型来调用相应的函数)。
而C语言编译后,妥妥地一个没有加工的函数名。
所以,要解决的问题是,如何让C++成功链接上(或者对应上,又或者找得上)C语言的符号。
测试一:c++ 同名的函数编译,然后反汇编查看符号表。
#include
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);
}
03
解决方案
使用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语言一样。
这样,就解决了符号的问题。
04
实验
实验一:同一个项目全部有源码,一次编译链接
//test.cpp
#include
#include "clib.h"
using namespace std;
int main()
{
int result;
result = add(1.1,2.0);
cout <"result: " <
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
extern "C"
{
#include "clib.h"
}
using namespace std;
int main()
{
int result;
result = add(1.1,2.0);
cout <"result: " <
return 0;
}
05
最后
c++想要调用c源码,但又是c源码遵循c编译器的规则,则要使用extern "C"。