文章目录

  • ​​1.C调用C++​​
  • ​​2.C++调用C​​

1.C调用C++

若C调用的是C++全部函数的代码

  • 将函数用extern "C"声明;;
    然后C代码中不要include C++的头文件, 而采用直接在C中增加函数声明的方式;
  • C调用C++,使用extern "C"则是告诉编译器依照C的方式来编译封装接口,当然接口函数里面的C++语法还是按C++方式编译

方法1(需要改动以前的C++代码):

C++代码
/*C++ code*/
extern "C" void f(int);
void f(int i)
{
// your code
}
C语言代码
void f(int); // 不引入, 而只是直接声明
void cc(int i)
{
f(i); //调用
// other code
}

若C调用的是C++成员函数的代码

  • 如果想要调用C++类中的成员函数, 由于C中没有类, 因此需要一个包装函数来调用这个成员函数, 并返回结果;
  • 如果你想要在C里调用成员函数(包括虚函数),则需要提供一个简单的包装(wrapper)
    方法2(需改动以前的C++代码):
// C++ code:
class C
{
// ...
virtual double f(int);
};

extern "C" double call_C_f(C* p, int i) // wrapper function
{
return p->f(i);
}
/* C code: 调用C::f()*/
double call_C_f(struct C* p, int i);
void ccc(struct C* p, int i)
{
double d = call_C_f(p,i);
/* ... */
}

方法3(不改动以前的C++代码):

  • C代码.c只能调用C的函数,所以可以用包裹函数去包裹C++函数,然后把这个包裹函数以C的规则进行编译,这样C就可以调用这个包裹函数了
C++代码
CppAdd.h
int cppadd(int x, int y);

CppAdd.cpp
#include "CppAdd.h"
#include <stdio.h>

int cppadd(int x, int y) {
printf("from C++ function.\n");
return (x + y);
}

编译静态库 libCppAdd.a
g++ -c CppAdd.cpp
ar -r libCppAdd.a CppAdd.o
C++代码(包裹函数)
CppAddWrapper.h
#ifdef __cplusplus
extern "C" {
#endif

int cppaddwrapper(int x, int y);

#ifdef __cplusplus
}
#endif

CppAddWrapper.cpp
#include "CppAddWrapper.h"
#include <stdio.h>
#include "CppAdd.h"

int cppaddwrapper(int x, int y) {
printf("from wrapper.\n");
int sum = cppadd(x, y);
return sum;
}

编译wrapper静态库 libCppAddWrapper.a
g++ -c CppAddWrapper.cpp
ar -r libCppAddWrapper.a CppAddWrapper.o
C代码
main.c
#include "CppAddWrapper.h"
#include <stdio.h>

int main()
{
int sum = cppaddwrapper(1, 2);
printf("1+2 = %d\n", sum);
return 0;
}

编译main,同时指定libCppAdd.a 和 libCppAddWrapper.a
gcc -o main main.c -L. -lCppAddWrapper -lCppAdd
或者把libCppAdd.a合并到libCppAddWrapper.a中
ar -x libCppAdd.a # 提取CppAdd.o
ar -x libCppAddWrapper.a # 提取CppAddWrapper.o
ar -r libCppAddWrapper.a CppAdd.o CppAddWrapper.o # 打包libCppAddWrapper.a
gcc -o main main.c -L. -lCppAddWrapper # 只需要连接libCppAddWrapper.a即可

2.C++调用C

方法1:

  • 在include的时候,要采用extern “C” 代码块形式.
  • 为什么使用extern “C”?因为cpp支持了函数重载,在编译生成的汇编代码中,会对函数名字进行命名改编,并不是C语言中的简单的函数名而已。
C++代码
// add.h整个头文件中的所有函数都是分布在多个xxx.c文件中的, 因此肯定xxx.obj是按照Ccompiler规则编译, 函数名无改动,

// 那么, C++中引用头文件的时候, 要在外部加上extern "C"包裹, 表示说我知道这部分是
//采用Ccompiler规则的函数名, 我会采用这个规则去找函数;

#include <iostream>
extern "C" {
#include "add.h" // 由add.h和add.c组成
}
using namespace std;

int main() {
cout << addTwoNumber(10, 20) << endl;
system("pause");
return 0;
}

方法2:

// C Code
void foo( int x );
C++这样调用C函数
// C++ Code
extern "C" void foo( int x );

方法3:

C语言代码
CAdd.h
int cadd(int x, int y);

CAdd.c
#include "CAdd.h"
#include <stdio.h>

int cadd(int x, int y) {
printf("from C function.\n");
return (x + y);
}

编译libCAdd.a
gcc -c CAdd.c # 生成CAdd.o
ar -r libCAdd.a CAdd.o # 归档生成libCAdd.a

编译动态库 libCAdd.so
gcc -shared -o libCAdd.so CAdd.c
C++代码
#include <stdio.h>

extern "C" {
#include "CAdd.h"
}

int main()
{
int sum = cadd(1, 2);
printf("1+2 = %d\n", sum);
return 0;
}

编译main
-l指定库名称,优先链接so动态库,没有动态库再链接.a静态库。
g++ -o cppmain cppmain.cpp -L. -lCAdd
若找不到动态库的搜索路径,则:export LD_LIBRARY_PATH=./设置当前路径为系统链接库目录就可以了

方法4:

  • 无论C还是C++直接正常include就可以使用了
#ifdef __cplusplus
extern "C" {
#endif

int cadd(int x, int y);

#ifdef __cplusplus
}
#endif

其他说明:

  • 时常在cpp的代码之中看到这样的代码: 特别是C ++中引入C的头文件,这些C头文件中出现很多如下代码
  • 其中__cplusplus是C++编译器的保留宏定义.就是说C++编译器认为这个宏已经定义了
  • extern "C"是告诉C++编译器,连接的话按照C的命名规则去找
#ifdef __cplusplus extern "C" 
{
#endif

//一段代码

#ifdef __cplusplus
}
#endif
  • .so动态库、.a静态库和.o中间文件的关系
    动态库链接和像静态库一样,告诉编译器函数的定义在这个库中;
    静态或者动态库文件其实就是对.o中间文件进行的封装,使用nm libadd.a命令可以查看其中封装的中间文件以及函数符号;
    链接静态库就是链接静态库中的.o文件(动态库类似),这和直接编译多个文件再链接成可执行文件一样;
  • 参考:如何用C语言封装 C++的类,在 C里面使用,C代码中如何调用C++ C++中如何调用C,​​C与C++代码如何互相调用​​,C调用C++库和C++调用C库的方法