C++:C与C++混合编程

混合编程

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

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

  • C++和C混合编程的困难所在
    (1)C++和C都是编译型语言,互相混合相对容易
    (2)难点:C++支持函数名重载,而C不支持,因此编译器生成目标文件时,函数名在目标文件中的临时内部名称规则不同。导致链接时符号对不上
    (3)解决方案:使用extern “C”{}; 让C++在对接的局部向C妥协兼容
    通用解决方案:在C的头文件中加extern "C"声明,在C++中直接包含头文件调用即可

  • 使用objdump工具来研究函数编译后的符号
    (1)写个典型的C语言库mylib.c和mylib.h,提供add和sub等几个函数
    (2)使用gcc -c -o编译得到库文件,再objdump -d反汇编得到.i文件
    (3)对比加不加extern "C"这2种情况下得到的.i文件的符号差异
    实验第1步:证明了C语言中名称为add的函数,编译后符号表中就叫add

gCC -c clib.c -o clib.o
objdump -d clib.o > clib.i
gCC -c clib.c -o clib2.o
objdump -d clib2.o > clib.i

在这里插入图片描述

实验第2步:证明了C++语言中名称为add的函数,编译后符号表中叫_Z3addii
分析:同样的源码,编译后生成的二进制代码其实是一样的,所以功能其实也是一样的
所以本质上是可以混合编程的,但是生成的中间符号名称不同,所以链接器难受
实验第3步:证明了在C++的头文件中,只要把C++的函数的声明放在extern “C”{}的大括号范围之内,就可以让g++在编译这个函数时生成中间符号名时按照C的规则而不是按照C++的规则,所以这样的函数就可以和C的库进行共同链接。


extern "C" {
void fun();
}

#if __cplusplus
extern "C" {
#endif

#if __cplusplus
}
#endif
  • #if __cplusplus: 这是一个预处理器指令,用于检查当前代码是否在 C++ 环境中编译。__cplusplus 是一个预定义的宏,它在编译 C++ 代码时被定义为一个年份值,比如 199711L 或更高。因此,#if __cplusplus 的作用是在编译时判断是否为 C++ 环境。

  • extern “C”: 这是 C++ 提供的一种语法,用于告诉编译器按照 C 的方式对待包裹在其中的代码。在 C++ 中,函数名会被编译器进行名称修饰(name mangling),以支持函数重载和命名空间等特性。而 C 中没有这些特性,函数名不会进行修饰。因此,当 C++ 调用 C 的函数时,需要使用 extern “C” 来告诉编译器按照 C 的方式来处理函数名,以便在链接时能够正确找到对应的函数。

  • #endif: 这是预处理器指令,表示条件编译的结束。与 #if 配对使用,用于结束条件编译的代码块

预编译

g++ -E main.cpp -o main.i

生产静态库

ar -r libclib.a clib.o
g++ main.cpp -lclib -L.

C调用C++库的方法

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

构建C++库

gcc cppadd.cpp -c -o cppadd.o
ar -r libcppadd.a cppadd.o

反编译查看信息

 objdump -d libcppadd.a > libcppadd.i

在这里插入图片描述
test.c

在这里插入代码片
extern int _Z3addii(int a,int b);

int main(void)
{
    _Z3addii(1,2);
    return 0;
}
gcc test.c -lcppadd -L.

解决方案:添加一层封装层

g++ cppaddwrapper.cpp -c -o cppaddwrapper.o -lcppadd -L.
ar -r libcppaddwrapper.a cppaddwrapper.o
objdump -d libcppaddwrapper.a > cppaddwrapper.i

在这里插入图片描述

 gcc test.c -lcppaddwrapper -lcppadd -L. 

因为一开始的add是这样编写的

int add(int a, int b) {
  cout << "a + b = " << a + b << endl;
  return 0;
}

#include "cppadd.hpp"的引入放在cppaddwrapper.cpp

还是会出现报错。提示有东西没有引入

/usr/bin/ld: ./libcppadd.a(cppadd.o): warning: relocation against _ZSt4cout' in read-only section .text’
/usr/bin/ld: ./libcppadd.a(cppadd.o): in function add(int, int)': cppadd.cpp:(.text+0x1f): undefined reference to std::cout’
/usr/bin/ld: cppadd.cpp:(.text+0x27): undefined reference to std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)' /usr/bin/ld: cppadd.cpp:(.text+0x3c): undefined reference to std::ostream::operator<<(int)’
/usr/bin/ld: cppadd.cpp:(.text+0x43): undefined reference to std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)' /usr/bin/ld: cppadd.cpp:(.text+0x4e): undefined reference to std::ostream::operator<<(std::ostream& (*)(std::ostream&))’
/usr/bin/ld: ./libcppadd.a(cppadd.o): in function __static_initialization_and_destruction_0(int, int)': cppadd.cpp:(.text+0x85): undefined reference to std::ios_base::Init::Init()’
/usr/bin/ld: cppadd.cpp:(.text+0xa0): undefined reference to `std::ios_base::Init::~Init()’
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

#include "cppadd.hpp"的引入放在cppaddwrapper.cpp
还是会出现报错。提示有东西没有引入

/usr/bin/ld: ./libcppaddwrapper.a(cppaddwrapper.o): in function addwrapper': cppaddwrapper.cpp:(.text+0x1d): undefined reference to add’
collect2: error: ld returned 1 exit status

总结

理解混合编程的存在性,知道解决方法

学习记录,侵权联系删除。
来源:朱老师物联网大课堂

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li星野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值