一、混合编程杂绪

1.1 为什么需要混合编程

  1. Cpp是从C演变过来的,C有很多优秀成熟项目和库,没必要在C++中重写,C++程序可以直接调用调用
  2. 庞大项目划分后一部分适合用C(底层),一部分适合用C++(中间层、上层)

1.2 混合编程的支撑

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

1.3 C++和C混合编程出现的问题

C++和C都是编译型语言,互相混合相对容易。但是:C++支持函数名重载,而C不支持,因此编译器生成目标文件时,函数名在目标文件中的临时内部名称规则不同,导致链接时符号对不上,如下所示:

创建测试文件:

    touch cadd.c cppadd.cpp add.h Makefile

源文件和头文件的内容如下,其中cadd.cappadd.c的内容都是一致的,均是库函数中声明的函数的定义

我们分别把.c源文件和.cpp源文件做成静态链接库然后把库进行反汇编

dec:objdump -d libcadd.a > libcadd.iobjdump -d libcppadd.a > libcppadd.i


对比得到的反汇编文件,可以发现同一个函数分别在gccg++下编译后生成的二进制代码其实是一样的。而临时内部名称是不同的:在gcc中,函数名保持不变;而g++中,还在函数名后增加了参数类型(add -> addii),如下图所示:

这时如果用一个.c文件和g++编译制作的静态库进行gcc编译链接或用一个.cpp文件和gcc编译制作的库进行g++编译链接都会链接错误:

#include "add.h"
int main(void)
{int x = 1, y = 1;add(x, y);return 0;
}


这就是因为主函数#include "add.h"中的add(预编译时直接替换相关内容)与静态库中的add用了不同的编译环境,导致标文件中的临时内部名称不同所致无法正确链接!

二、C和C++混合编程的解决方案

通过前面的研究我们可以知道C和C++本质上是可以混合编程的,但是生成的中间符号名称不同,所以链接器在进行链接时会出问题,那如何解决呢?

2.1 先谈谈__cplusplus

__cplusplus是C++中预定义的一个标准宏,为长整型,其值为cpp标准的年月;在C中是未进行定义的,可以用来检测编译环境

#include <stdio.h>int main(void)
{#ifdef __cplusplusprintf("G++ %ld.\n", __cplusplus);#elseprintf("GCC.\n");#endifreturn 0;
}

2.2 extern "C"{}

使用extern "C"{}可以使{}中的内容用C的标准来编译,我们将1.3中的add.h中的内容改为:

#ifndef __ADD_H__
#define __ADD_H__extern "C"
{int add(int a, int b);}#endif

然后和cppadd.cpp一起制作成静态库然后反编译,我们可以发现虽然用的g++进行编译,反编译得到的文件中add的临时名称还是add,而不是_Z3addii

2.3 解决方案

通过2.2我们可以知道在C++的头文件中只要把函数的声明放在extern "C"{}的大括号范围之内,就可以让g++在编译这个函数时生成中间符号名时按照C的规则而不是按照C++的规则,所以这样的函数就可以和C的库进行共同链接。但是,在C语言中,extern "C"{}是未被定义的

所以我们将__cplusplus和extern “C”{}结合使用,通常有以下两种情况:

三、两种常见情况

3.1 同一个项目中C是库,C++是源码,C++调用C

这是最常见的情况,我们只需要在C的头文件中加上extern "C"的声明,在C++中直接包含头文件调用。后继直接用g++编译链接即可,如下所示(mian.cpp和C库均来自1.3):

add.h中的内容更改如下:

#ifndef __ADD_H__
#define __ADD_H__#ifdef __cplusplus
extern "C"
{#endifint add(int a, int b);#ifdef __cplusplus
}
#endif#endif

return.sh中内容如下:

#!/bin/shg++ main.c -lcadd -L. -o main.elf
./main.elf
echo $?

根据结果我们可以看到程序进行了成功的编译、链接、执行:

如果我们同时有C和Cpp的源码,我们也是只需要在C的.h库函数中加入__cplusplusextern "C"{},对于Cpp的.hpp文件不做任何改动,最后统一进行g++编译即可

3.2 同一个项目中C++是库,C是源码,C调用C++

如果我们只有C++库而源码是C,我们最后只能用gcc编译链接,这带来了很多麻烦:

  1. g++gcc的编译时符号差异
  2. c++支持很多c并不支持的特性,如函数重载

而C++继承了C的所有特性,也就是说C编译时无法直接像C++一样使用特定的语句实现编译Cpp时按照C的规则。但是,我们可以用cpp写一层封装层,加上extern "C",用g++编译成静态库,这个时候我们在C源码中就可以直接调用我们的静态库,用gcc编译链接!

我们还是使用1.3中制作的静态库,我们检查发现此时libcppadd.aadd的临时符号是_Z3addii

我们制作封装层,创建文件

touch cppaddwrapper.cpp cppaddwrapper.hpp

其中,cppaddwrapper.hpp中的内容如下:

#ifndef __CPP_ADDWRAPPER_HPP__
#define __CPP_ADDWRAPPER_HPP__#ifdef __cplusplus
extern "C"
{#endifint addwrapper(int a, int b);#ifdef __cplusplus
}
#endif#endif

cppaddwrapper.cpp中的内容如下

#include "add.hpp"
#include "cppaddwrapper.hpp"int addwrapper(int a, int b)
{add(a, b);
}

我们只需要在这个函数中对add进行调用即可,我们将封装层用g++ar制作成静态封装库,再进行反编译,结果如下:

因为在addwrapper函数的声明中加了extern "C",故临时符号没有改变。由于用的是g++编译,而该头文件包含中的add.hppadd函数没有extern "C"声明,所以调用时使用的add的临时名称还是会变成_Z3addii,和静态库中的临时名称相匹配,所以在反汇编代码callq 1d <addwrapper+0x1d>中发生了跳转。第一个参数1d就是add函数的地址入栈,尖括号里面的是偏移地址,因为我们使用的是静态库,无需重定位,所以偏移地址也是0x1d

这个时候,我们就可以在我们自己的c文件中直接调用静态封装库中的函数,和封装库一起用gcc进行链接了!如下建立测试文件夹,将头文件和静态库移入文件夹中:

main.c中的测试代码如下:

#include "cppaddwrapper.hpp"int main(void)
{addwarpper(1, 1);return 0;
}

shell脚本中的内容如下:需要注意的是由于封装层的静态库是使用了C++库的,所以在链接的时候也需要用-lxx指定静态库

#!/bin/shgcc main.c -lcppaddwrapper -lcppadd -L. -o main.elf
./main.elf
echo $?

根据main函数的返回值我们可以知道程序成功地进行了编译链接执行

在只有C++库而源码是C的情况下,使用封装层的技巧就是封装层使是用g++编译,但是只在封装头文件函数声明中加了extern "C",而调用的C++静态库函数的#include未加,因此调用的函数还是按C++的规则解析,制作库时可以实现连接。在C源码中调用封装库,巧妙避免了直接使用C++库由于临时符号不同导致的链接错误

【Cpp】C和C++混合编程相关推荐

  1. matlab两个多项式相除,C++和MATLAB混合编程求解多项式系数(矩阵相除)

    摘要:MATLAB对于矩阵处理是非常高效的,而C++对于矩阵操作是非常麻烦的,因而可以采用C++与MATLAB混合编程求解矩阵问题. 主要思路就是,在MATLAB中编写函数脚本并使用C++编译为dll ...

  2. C和C++混合编程的Makefile的编写!

    在项目实践中,经常遇到C和C++混合编程的情况. 目前的业务需求是: c写的几个文件,和一个C++文件要整合为一个动态库,被C++调用.而这个动态库的生成过程中,会链接几个基础的开发库,比如libz, ...

  3. matlab混合编程设置,matlab c++ 混合编程初始设置

    以前做过matlab7与c++的混合编程:将m函数编译成dll给C++调用,从而加快开发的进度.但是今天在matlab2008b 下面又做了一遍,发现matlab又改了很多东西,诸如增加了面向对象的的 ...

  4. python利器怎么编程-C++和Python混合编程的利器

    Python是一种简单易学.功能强大的编程语言,它有高效率的高层数据结构,能简单而有效地实现面向对象编程.Python简洁的语法.对动态输入的支持和解释性语言的本质,使得它在很多领域的大多数平台上都是 ...

  5. extern quot;Cquot; 的含义:实现C++与C及其他语言的混合编程

    C++中extern "C"的设立动机是实现C++与C及其他语言的混合编程. C++为了支持函数的重载.C++对全局函数的处理方式与C有明显的不同. 对于函数void foo( i ...

  6. matlab和C/C++混合编程--Mex

    最近的项目需要matlab和C的混合编程,经过一番努力终于完成了项目要解决的问题.现在就将Mex的一些经验总结一下,当然只是刚刚开始,以后随着学习的深入继续添加.首先讲讲写Mex的一些常规规定,然后我 ...

  7. 在Matlab中使用mex函数进行C/C++混合编程

    最近写了个Matlab程序,好慢呐--所以开始学习Matlab与C/C++混合编程.下面写了个测试代码,显示一个Double类型矩阵中的元素. 源代码 [cpp] view plaincopyprin ...

  8. STM32 进阶教程 7 -  C与C++混合编程

    前言 在嵌入式开发过程中经常会用到第三个芯片/设备,这些第三方的芯片提供的DEMO程序或驱动程序有时候是C++,如果先前的系统用C语言写的就会出现点问题,往往需先将C++的驱动程序自已手动改成C语言再 ...

  9. 第五天2017/04/06(下午1:C、C++混合编程 与 #ifdef __cplusplus extern C{ })

    C++.C编译器对函数编译的相关知识:C++支持函数重载,而C不支持,两者的编译规则也不一样.函数被C++编译后在符号库中的名字与C语言的不同.例如,假设某个函数的原型为: void foo( int ...

  10. C++和MATLAB混合编程-DLL篇

    先小话一下DLL,DLL是动态链接库,是源代码编译后的二进制库文件和程序接口,和静态链接库不同的是,程序在编译时并不链接动态链接库的执行体,而是在文件中保留一个调用标记,在程序运行时才将动态链接库文件 ...

最新文章

  1. 请问如何更改dedecms“文件保存目录”的字符限制 ?
  2. InputStream,BufferedImage与byte数组之间的转换
  3. linux命令详解——iostat
  4. 高质量C++编程指南
  5. Python学习日志(二)
  6. 世界3大IT服务品牌之一塔塔咨询服务公司发布新品牌宣言
  7. gfsk调制频谱_gfsk调制方式
  8. 中仪股份管道机器人_中仪股份中仪股份cctv检测管道机器人X5-HSX5-HS
  9. 机器学习算法工程师面试考点汇总
  10. Unexpected console statement
  11. 用户注册+登录(下)
  12. ps4变更账号服务器,PSN ID怎么改?详细步骤教你如何更改PS4的PSN ID
  13. PHP 实现递归处理数据
  14. 智能匹配 — 百度SEM
  15. May 17th Thursday (五月 十七日 木曜日)
  16. B - 爆零(×)大力出奇迹(√) HDU - 2093
  17. 程序员必知的编程4大祖师爷,C语言之父堪称编程界的牛顿!
  18. 你应该要懂的宇宙真相——《给忙碌者的天体物理学 》下篇
  19. matlab 绘图常用命令
  20. win10笔记本电脑分享热点无法选择2.4ghz或5ghz的解决方法

热门文章

  1. Java参数校验工具validation
  2. arm应用程序之文件读写操作差异open与fopen
  3. 服务器中的软件如何备份文件夹在哪里找,itunes备份文件在哪,如何找到itunes的备份文件...
  4. [Bada开发]使用共享库
  5. avr 74hc595驱动数码管动态显示c语言例程,ATmega8驱动74HC595程序
  6. matlab2016 wavread,matlabwavread用法
  7. 【Keras】使用 TPU 训练 TensorFlow 模型
  8. 336亿的生意——你所不了解的Dapp这一年(下)
  9. (一)深度学习入门之单个神经元
  10. mysql堆溢出_MySQL错误1436:线程堆栈溢出,带有简单查询