解决C/C++相互调用

概述:比较详细的解释C/C++混合编译的原理,基于GNU工具演示。

基础知识:GNU工具链的简单使用,能明白 gcc -o -c这样的命令就行了,自己调试运行一次代码就能明白。如果只是照这个别人的代码,写了一个中间层去满足混合编译的需求,最后你还是会把自己绕晕。

看看这个或许就恍然大悟:GCC手册

先要把这两个错误的想法彻底弄明白,才能把混合编译弄清楚。

错误1:gcc只能编译c,g++只能编译c++。

错误2:使用g++编译链接__cplusplus才会定义。

编译器和源文件的关系:通过以下的分析可以推导出这个表的,彻底理解extern  "C"的处理机制。 

源文件编译命令编译结果__cplusplus是否定义默认语法规则C++库链接
source.cgcc 成功C 
source.cppgcc成功c++\函数重载自动链接
source.cg++成功c++\函数重载手动指定
source.cppg++成功c++\函数重载自动链接

 

造成C/C++混合编译失败的根本原因分析

  1. g++、gcc标准库链接
  2. C++函数重载机制反汇编分析
  3. c/c++相互调用处理机制

1、g++、gcc标准库链接

#ifdef __cplusplus
#include <iostream>
using namespace std;
int flag = 1;
#else
#include "stdio.h"
int flag = 0;
#endif

int main(void)
{
#ifdef __cplusplus
	cout<<"c++ code"<<flag<<endl;
	cout<<"__cplusplus:"<<__cplusplus<<endl;
#else
	printf("C code %d \r\n",flag);
#endif
	return 0;
}

1、gcc编译C语言:常规操作,符合思路。__cplusplus无定义

将上述测试代码命名为gcc_c.c文件,编译运行,我相信你能理解,不理解下面就不用看了。

2、gcc编译C++:__cplusplus有定义。感觉有点奇怪,有时候行,有时候又不行。

         gcc不会自动链接C++的标准库文件,若source.cpp文件中调用了C++标准库,使用gcc  source.cpp。这条命令编译会失败。但是手动链接c++标准库,使用gcc source.cpp -lstdc++ 这条命令就可以编译成功。将上述测试代码命名为gcc_cpp.cpp的C++源文件,gcc gcc_cpp.cpp -o gcc_cpp编译试一下,编译不了,链接不到标准库。

        如果source.cpp文件中,没有使用任何库文件,都是原生的语法,那gcc source.cpp会根据C++规则编译链接(也就是不需要手动指定链接库)成功。

        将gcc_cpp.cpp文件生成gcc_cpp.o目标文件,运行gcc -c gcc_cpp.cpp,是可以成功生成的,但是运行gcc gcc_cpp.o -o gcc_cpp命令,又会出现上图一样的错误。

       再手动指定C++的标准库,将gcc_cpp.o文件生成gcc_cpp可执行程序,运行命令 gcc  gcc_cpp.o -o gcc_cpp -lstdc++。

运行./gcc_cpp成功,如下图所示。

3、g++编译C源文件:__cplusplus有定义。

完全可行,C++本来就兼容C语言,但是不推荐这么使用,尤其不要在CPP源文件使用C语言特性。

测试一下,g++生产的目标文件,使用gcc工具去链接生产可执行程序。

测试结果:如下图,执行失败,不能链接C++标准库。手动指定C++标准库,就可以成功生成运行。

4、g++编译c++:__cplusplus有定义,常规操作。

其实也是先调用gcc编译成目标文件,然后使用g++链接。

2、C++函数重载机制反汇编分析

1、gcc编译c源文件:__cplusplus无定义。使用C语言特性编译,无重载机制。

2、gcc编译C++源文件:__cplusplus有定义。使用C++语言特性,函数重载。

3、g++编译c源文件:__cplusplus有定义。使用C++语言特性,函数重载。

4、g++编译C++源文件:__cplusplus有定义。使用C++语言特性,函数重载。

3、c/c++相互调用处理机制

1、C调用C++:要解决什么问题?C++函数重载,C语言调用时如何寻找到对应的被调用函数。找不到就链接失败,无法生成可执行程序。

解决方案:添加一个中间层,把重载函数封装成不同名函数,使用C语言规则编译链接中间层。

被调用的C++代码:func.cpp 、func.h

#include <iostream>
using namespace std;

//函数重载
int add(int a,int b)
{
	cout<<"int add() is called!"<<endl;
	return a+b;
}
float add(float a,float b)
{
	cout<<"float add() is called!"<<endl;
	return a+b;
}
#ifndef _FUNC_H
#define _FUNC_H

int add(int a,int b);
float add(float a,float b);
#endif

中间层:interface.cpp、interface.h

#include "interface.h"
#include "func.h"

int Interface_AddInt(int a,int b)
{
	return add(a,b);
}
float Interface_AddFloat(float a,float b)
{
	return add(a,b);
}
#ifndef _INTERFACE_H
#define _INTERFACE_H

#ifdef __cplusplus
extern "C"
{
#endif
int Interface_AddInt(int a,int b);
float Interface_AddFloat(float a,float b);
#ifdef __cplusplus
}
#endif
#endif

main.c调用

#include "stdio.h"
#include "interface.h"
int main(void)
{
    printf("1+1 : %d \r\n",Interface_AddInt(1,1) );
    printf("1.1+1.1 :%f\r\n",Interface_AddFloat(1.1,1.1));
    return 0;
}

编译运行:gcc main.c interface.cpp func.cpp -lstdc++

思考:中间层本质上是如何起作用的?

        将 extern “C”注释后,如下图所示,main.c调用中间层时,找不到函数了。是因为根据C++的默认编译规则,中间层的函数名也被重载处理了,函数名不一样了。使用extern C 强制interface按照C语言规则进行编译链接,就可以成功生成可执行程序。

#ifndef _INTERFACE_H
#define _INTERFACE_H

// #ifdef __cplusplus
// extern "C"
// {
// #endif
int Interface_AddInt(int a,int b);
float Interface_AddFloat(float a,float b);
// #ifdef __cplusplus
// }
// #endif
#endif

2、C++调用C

mian.cpp

#include <iostream>
#include "myc.h"
using namespace std;

int main(void)
{
	cout<<"add:"<<	add(1,1)<<endl;
	cout<<"__cplusplus:"<<__cplusplus<<endl;
	return 0;
}

myc.c

int add(int a, int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}

myc.h

#ifndef _MYC_H_
#define _MYC_H_
	int add(int a, int b);
	int sub(int a,int b);
#endif

情况1:g++编译链接main.cpp和myc.c,可成功生成可执行程序。但是myc.c使用C++规则编译,如下图。

情况2:gcc编译myc.c生成myc.o,g++编译main.cpp生成main.o,再用gcc/g++链接成可执行程序.

gcc编译myc.c使用C语言规则,gcc默认链接 gcc main.o myc.o会失败,因为gcc不会自动去链接C++的库。

但是手动指定gcc main.o myc.o -lstdc++,依然失败,因为链接不到add这个函数的地址。

解决方案:使用extern "C"{ },指示这一部分是C代码,要以兼容C的方式去处理,不能当成C++的代码。

#ifndef _MYC_H_
#define _MYC_H_

#ifdef __cplusplus
extern "C"
{
#endif
	int add(int a, int b);
	int sub(int a,int b);
#ifdef __cplusplus
}
#endif
#endif

如下图,使用gcc和g++来链接生成可执行文件都可以,只是使用gcc不会自动链接c++标准库,要手动指定。

gcc和g++都行?是不是晕?

小结:在一般的项目开发中,使用make组织代码,都是把C源文件使用xx_gcc(交叉编译工具)根据C语言规格汇编成目标文件c_source.o,把cpp源文件汇编成cpp_source.o,再使用xx_ld链接成可执行程序。所以,一个项目是C/C++混合编译,那被C++所用调用的C头文件,都要加上extern “c"来兼容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值