【从C到C++】------- C和C++混合编程之 extern “C“


C与C++混合编程引入


【1】为什么需要混合编程

  • C有很多优秀成熟项目和库,丢了可惜,重写没必要,C++程序里要调用
  • 庞大项目划分后一部分适合用C,一部分适合用C++
  • 其他情况,如项目组一部分人习惯用C,一部分习惯用C++

【2】为什么不同语言可以混合编程

  • 程序编译过程:源文件->目标(库)文件->可执行程序->镜像文件
  • 任何编程语言执行时都必须是可执行程序,所以都必须先被编译成目标文件
  • 混合编程的“混合”操作发生在链接这一步

【3】C++和C混合编程的困难

  • C++和C都是编译型语言,互相混合相对容易
  • 难点:C++支持函数名重载,而C不支持,因此编译器生成目标文件时,函数名在目标文件中的临时内部名称规则不同。导致链接时符号对不上
  • 解决方案:使用extern “C”{}; 让C++在对接的局部向C妥协兼容

【4】使用objdump工具来研究函数编译后的符号

  • 写个典型的C语言库clib.c和clib.h;提供简单的add函数
  • 使用gcc -c -o编译得到库文件,再objdump -d反汇编得到.i文件

在这里插入图片描述

在这里插入图片描述

  • 对比加不加extern "C"这2种情况下得到的.i文件的符号差异
    在这里插入图片描述
    实验第1步:证明了C语言中名称为add的函数,编译后符号表中就叫add
    实验第2步:证明了C++语言中名称为add的函数,编译后符号表中叫_Z3addii
    同样的源码,编译后生成的二进制代码其实是一样的,所以功能其实也是一样的所以本质上是可以混合编程的,但是生成的中间符号名称不同,所以链接器难受。

在这里插入图片描述
实验第3步:证明了在C++的头文件中,只要把C++的函数的声明放在extern “C”{}的大括号范围之内,就可以让g++在编译这个函数时生成中间符号名时按照C的规则而不是按照C++的规则,所以这样的函数就可以和C的库进行共同链接。


C与C++混合编程的可能情况分析解决


【1】实际开发中会有以下三种情况

  • 同一个项目全部有源码即C源码和C++源码,一次编译链接。
  • 同一个项目中C是库,C++是源码,C++调用C
  • 同一个项目中C++是库,C是源码,C调用C++

1.C和C++都有源码的情况


【1】现有clib.c,clib.h,main.cpp,c和c++都有源码,并且在main.cpp中调用clib.c的函数

/*------clb.c-----------*/
#include "clib.h"

int add(int a, int b)
{
	return a + b;
}
/*------clb.h-----------*/
#ifndef     __CLB_H__
#define     __CLB_H__	

#ifdef      __cplusplus
extern "C"
{
#endif

//函数声明
int add(int a, int b);

#ifdef      __cplusplus
}
#endif

#endif
//CPP文件中调用C的函数
/*------main.cpp--------*/
#include "clib.h"
#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
	int x = 3, y = 5, ret = 0;
	ret = add(x,y);

	cout << "ret = " << ret <<endl;
	return 0;
}

【2】解决办法1:在C的头文件中加extern "C"声明,全部使用g++编译,可行,不推荐。

在这里插入图片描述

【3】解决办法2:在C的头文件中加extern "C"声明,先用gcc编译C文件生成.o文件再用g++编译生成最终执行文件。

在这里插入图片描述


2.C++调用C库的情况


【1】这种是最典型的常见情况,C库一般都包含头文件
【2】】现模拟用clib.c制作成静态库libclib.a,提供clib.h,main.cpp,并且在main.cpp中调用clib.c的函数

/*------clb.c-----------*/
#include "clib.h"

int add(int a, int b)
{
	return a + b;
}
  • 制作静态库

在这里插入图片描述

/*------clb.h-----------*/
#ifndef     __CLB_H__
#define     __CLB_H__	

//函数声明
int add(int a, int b);

#endif
//CPP文件中调用C的函数
/*------main.cpp--------*/
#include "clib.h"
#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
	int x = 3, y = 5, ret = 0;
	ret = add(x,y);

	cout << "ret = " << ret <<endl;
	return 0;
}

【3】通用解决方案1:在C的头文件中加extern "C"声明,在C++中直接包含头文件调用即可

/*------clb.h-----------*/
#ifndef     __CLB_H__
#define     __CLB_H__	

#ifdef      __cplusplus
extern "C"
{
#endif

//函数声明
int add(int a, int b);

#ifdef      __cplusplus
}
#endif

#endif

【4】解决方案2:在CPP文件中,包含C文件的头文件前加extern "C"声明

//CPP文件中调用C的函数
/*------main.cpp--------*/
#ifdef      __cplusplus
extern "C"
{
#endif

#include "clib.h"

#ifdef      __cplusplus
}
#endif

#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
	int x = 3, y = 5, ret = 0;
	ret = add(x,y);

	cout << "ret = " << ret <<endl;
	return 0;
}

【5】用g++编译连接,成功执行
在这里插入图片描述


3.C调用C++库的情况


【1】C调用C++的麻烦

  • g++和gcc的编译时符号差异
  • c++支持很多c并不支持的特性,如函数重载
  • 解决方案:添加一层封装层

【2】代码实战:C调用C++库中的函数

  • 用cpp写一个库,cppadd.cpp cppadd.hpp,用g++编译成静态库
/*--------cppadd.cpp---------*/
#include "cppadd.hpp"

int add(int a, int b)
{
	return a + b;
}
/*--------cppadd.hpp---------*/
#ifndef     __CPPADD_HPP__
#define     __CPPADD_HPP__	

//函数声明
int add(int a, int b);

#endif

在这里插入图片描述

  • objdump反编译库,查看确认符号
    在这里插入图片描述

  • 用cpp写一个封装层,用上extern “C”,cppaddwrapper.cpp和cppaddwrapper.hpp,用g++编译成静态库

/*---------cppaddwrapper.hpp-----------*/
#ifndef __CPPADDWRAPPER_HPP__
#define __CPPADDWRAPPER_HPP__

#ifdef __cplusplus
extern "C"
{
#endif

int addwrapper(int a, int b);

#ifdef __cplusplus
}
#endif

#endif
/*---------cppaddwrapper.cpp-----------*/
#include "cppaddwrapper.hpp"
#include "cppadd.hpp"    //要包含库的头文件

int addwrapper(int a, int b)   //中间层的函数参数要和要调用的函数的参数一致
{
	add(a,b);
}
//把封装层也要做成库
g++ cppadd_wrapper.cpp -c -o cppaddwrapper.o -lcppadd -L.
ar -r libcppaddwrapper.a libcppaddwrapper.o
  • objdump反编译库,查看确认符号
    在这里插入图片描述

  • 用c写一个test.c,调用我们制作的中间层库,用gcc编译链接,运行查看结果

/*---------test.c-----------*/
#include "cppaddwrapper.hpp"
#include <stdio.h>

int main(int argc, char const *argv[])
{
	int x = 4, y = 5, ret = 0;
	ret = addwrapper(x,y);

	printf("ret = %d\n", ret);
	
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值