由于历史原因,以及不同开发人员的技术偏好,C语言和C++语言都有一些独有的非常有价值的项目,因而两种语言的互操作,充分利用前人造的轮子是一件非常有价值的事情。

C++代码调用C代码很简单,只要分别在包含的C头文件的开头和结尾加上如下的两个块:

#ifdef __cplusplus

extern "C" {

#endif

#ifdef __cplusplus

}

#endif

即可。

然而为了支持类、重载等更加高级的特性,在编译C++代码时,C++符号会被修饰。我们dump Linux平台加密库 libcrypto++ 的符号表,可以看到如下的内容:

$ readelf -s /usr/lib/libcrypto++.so

Symbol table '.dynsym' contains 9607 entries:

Num: Value Size Type Bind Vis Ndx Name

0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND

1: 00000000001daa58 0 SECTION LOCAL DEFAULT 9

2: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND _ZTIi@CXXABI_1.3 (2)

3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_location@GLIBC_2.2.5 (3)

4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt18uncaught_exceptionv@GLIBCXX_3.4 (4)

5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt8__detail15_List_node_base7_M_hookEPS0_@GLIBCXX_3.4.15 (5)

6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getservbyname@GLIBC_2.2.5 (6)

7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bind@GLIBC_2.2.5 (6)

8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_@GLIBCXX_3.4 (4)

9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __longjmp_chk@GLIBC_2.11 (7)

10: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND _ZTIh@CXXABI_1.3 (2)

11: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND _ZTVSt9basic_iosIcSt11char_traitsIcEE@GLIBCXX_3.4 (4)

12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND socket@GLIBC_2.2.5 (6)

13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt14basic_ifstreamIcSt11char_traitsIcEED1Ev@GLIBCXX_3.4 (4)

. . . . . .

86: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSo5writeEPKcl@GLIBCXX_3.4 (4)

87: 0000000000000000 0 FUNC GLOBAL DEFAULT UND malloc@GLIBC_2.2.5 (6)

88: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt9basic_iosIcSt11char_traitsIcEE4initEPSt15basic_streambufIcS1_E@GLIBCXX_3.4 (4)

89: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSi5seekgElSt12_Ios_Seekdir@GLIBCXX_3.4 (4)

90: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_key_delete@GLIBC_2.2.5 (3)

91: 0000000000000000 0 FUNC GLOBAL DEFAULT UND shutdown@GLIBC_2.2.5 (6)

92: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt15set_new_handlerPFvvE@GLIBCXX_3.4 (4)

93: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_getspecific@GLIBC_2.2.5 (3)

94: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strcmp@GLIBC_2.2.5 (6)

95: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtol@GLIBC_2.2.5 (6)

96: 0000000000000000 0 FUNC GLOBAL DEFAULT UND ioctl@GLIBC_2.2.5 (6)

. . . . . .

186: 00000000002c5a80 142 FUNC GLOBAL DEFAULT 12 _ZN8CryptoPP6xorbufEPhPKhS2_m

187: 00000000002fd6d0 9 FUNC WEAK DEFAULT 12 _ZN8CryptoPP21InvertibleRSAFunction9BERDecodeERNS_22BufferedTransformationE

188: 00000000001ea840 73 FUNC GLOBAL DEFAULT 12 _ZN8CryptoPP13Base64Decoder22GetDecodingLookupArrayEv

189: 0000000000249760 6 FUNC WEAK DEFAULT 12 _ZThn8_N8CryptoPP13DL_SignerImplINS_25DL_SignatureSchemeOptionsINS_5DL_SSINS_13DL_Keys_ECDSAINS_4EC2NEEENS_18DL_Algorithm_ECDSAIS4_EENS_37DL_SignatureMessageEncodingMethod_DSAENS_6SHA256EiEES5_S7_S8_S9_EEED0Ev

190: 0000000000278b60 86 FUNC WEAK DEFAULT 12 _ZN8CryptoPP8Rijndael3DecD1Ev

191: 00000000001fd1f0 2 FUNC WEAK DEFAULT 12 _ZN8CryptoPP23DefaultEncryptorWithMAC8FirstPutEPKh

192: 000000000026a490 51 FUNC GLOBAL DEFAULT 12 _ZN8CryptoPP23FilterWithBufferedInputC2EPNS_22BufferedTransformationE

193: 0000000000285180 6 FUNC WEAK DEFAULT 12 _ZNK8CryptoPP8GCM_Base6IVSizeEv

194: 000000000032e830 510 FUNC WEAK DEFAULT 12 _ZN8CryptoPP18StandardReallocateItNS_20AllocatorWithCleanupItLb0EEEEENT0_7pointerERS3_PT_NS3_9size_typeES8_b

195: 00000000002a1790 185 FUNC WEAK DEFAULT 12 _ZSt18uninitialized_copyISt15_Deque_iteratorIyRKyPS1_ES0_IyRyPyEET0_T_S9_S8_

196: 0000000000355610 25 OBJECT WEAK DEFAULT 14 _ZTSN8CryptoPP11RSAFunctionE

. . . . . .

这与我们在源文件和头文件里看到的那些函数、类的声明定义都不一样。通过binutils的工具c++filt demangle这些符号可以让我们看到它们在代码里的样子:

$ c++filt _ZTSN8CryptoPP11RSAFunctionE

typeinfo name for CryptoPP::RSAFunction

$ c++filt _ZN8CryptoPP18StandardReallocateItNS_20AllocatorWithCleanupItLb0EEEEENT0_7pointerERS3_PT_NS3_9size_typeES8_b

CryptoPP::AllocatorWithCleanup::pointer CryptoPP::StandardReallocate >(CryptoPP::AllocatorWithCleanup&, unsigned short*, CryptoPP::AllocatorWithCleanup::size_type, CryptoPP::AllocatorWithCleanup::size_type, bool)

那到底有没有办法在C代码中调用C++代码呢?方法当然是有的,而且还不止一种。

通过extern “C”调用

在 .cpp 文件中定义一个函数,声明为extern "C",则该函数可以方便地在C代码中调用。由于该函数在 .cpp 文件中定义,因而在该函数的实现中,可以调用任意的C++代码,包括C++函数,创建C++类等等。

C++头文件:

#ifndef CPPFUNCTIONS_H_

#define CPPFUNCTIONS_H_

#ifdef __cplusplus

int cpp_func(int input);

extern "C" {

#endif

int c_func(int input);

#ifdef __cplusplus

}

#endif

#endif /* CPPFUNCTIONS_H_ */

C++实现文件如下:

#include "CppFunctions.h"

int cpp_func(int input) {

return 5;

}

int c_func(int input) {

return cpp_func(input);

}

在C代码里调用C++函数:

#include

#include "CppFunctions.h"

int main(int argc, char **argv) {

printf("%d\n", c_func(10));

return 0;

}

在C++文件里定义的c_func函数就像一座桥一样,连接了C代码的世界和C++代码的世界。但 C 函数c_func的参数及返回值的类型自然是受到一定的限制的,但在函数实现中可以适配要调用的C++接口,做一些适配。

通过dlopen/dlsym调用

借助于在 .cpp 文件中定义的C函数,间接地调用C++接口,固然是能实现在 C 代码中调用C++代码的目标,然而还是有些麻烦。通过libdl提供的接口,可以使我们的目标通过更简便的方式实现。

为dlsym传入经过修饰的符号,可以找到对应的函数的地址。

通过如下命令将上面的CPPFunctions.cpp文件编译为一个动态链接库:

$ gcc -shared -fPIC CPPFunctions.cpp -o libCppLibTest.so

通过dlopen和dlsym找到对应的C++函数,并将其强制类型转换为适当类型的函数指针,然后通过函数指针调用目标函数,如:

#include

#include

int main(int argc, char **argv) {

void *libCPPTest = dlopen("/home/hanpfei0306/workspace_java/CppLibTest/Debug/libCppLibTest.so", RTLD_NOW);

int (*cpp_func)(int) = (int (*)(int))dlsym(libCPPTest, "_Z8cpp_funci");

printf("cpp_func = %p\n", cpp_func);

printf("cpp_func output = %d\n", cpp_func(10));

return 0;

}

编译并执行上面的代码,在我的机器上可以看到如下的输出:

cpp_func = 0x7f35727a8650

cpp_func output = 5

总结

以上就是这篇文章的全部内容了,希望本文的的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

c语言调用c 方法,C语言代码中调用C++代码的方法示例相关推荐

  1. c语言代码中调用系统命令行.sh shell脚本,linux shell system传参

    C语言代码中调用命令行: 1. 使用system(" 命令行 ");    --  执行完命令行后,会返回原先C代码的位置,继续执行. 2. 如果命令行中需要传参,使用 sprin ...

  2. Golang cgo:如何在Go代码中调用C语言代码?

    如何在Go代码中调用C语言代码? Go语言是通过自带的一个叫CGO的工具来支持C语言函数调用,同时我们可以用Go语言导出C动态库接口给其它语言使用. 方式一.直接在 Go 代码中写入 C 代码 检查是 ...

  3. 【Unity3D】Android Studio 工程中使用 Java 代码调用 Unity 的 C# 脚本 ( Java 中调用 UnityPlayer#UnitySendMessage 方法 )

    文章目录 一. Java 调用 C# 依赖库准备 1.依赖库位置 2.unityLibrary 依赖库位置 二. Java 调用 C# 的 UnityPlayer#UnitySendMessage 方 ...

  4. 在python代码中调用vba宏的四种方法

    在python代码中调用vba宏 工作以python为主体,但是遇到了一些word操作的需求(详见上一篇),这个需求用word自带的功能会很容易实现,于是就想着能不能用python调用宏来处理. 网上 ...

  5. 调用c++_WebAssembly: 在C代码中调用JS的函数

    0. 前提知识点 导出C中的函数给JS调用:主要是EMSCRIPTEN_KEEPALIVE这个Emscripten环境特有的宏. #include <stdio.h>#ifndef EM_ ...

  6. vue怎么调用子元素的方法_vue 父组件中调用子组件函数的方法

    vue 父组件中调用子组件函数的方法 在父组件中调用子组件的方法: 1.给子组件定义一个ref属性.eg:ref="childItem" 2.在子组件的methods中声明一个函数 ...

  7. 为什么 wait 方法要在 synchronized 中调用?

    作者:Yujiaao 来源:https://segmentfault.com/a/1190000019962661 一个有难度的 Java 问题,wait 和 notify. 它们是在有 synchr ...

  8. 安卓代码中常用的代码以及问题收集

    EnglishVersion ->_->:https://raw.githubusercontent.com/jiang111/awesome-android-tips/master/RE ...

  9. 实验四:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    贺邦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验目的: 使用库函数 ...

最新文章

  1. puppet(1.7-2.1)
  2. wordcount代码_Scala小案例之wordcount
  3. PostgreSQL操作问题(转载)
  4. Linux ENSP 搭建DHCP服务器并实现中继和Linux 下搭建DNS服务器(2个实验可跟做)
  5. 约瑟夫环 猴子选大王的问题
  6. java 图形绘制_Java Graphics 图形绘制
  7. 算法--排序--大小写字母数字分离(桶排序思想)
  8. mxnet深度学习(Symbol)
  9. aspell_如何使用Aspell在Linux命令行上检查拼写
  10. 信息必填的php,php - {“成功”:0,“消息”:“必填字段丢失”}在本地主机上测试文件 - 堆栈内存溢出...
  11. DAY8-Nessus漏洞扫描
  12. day9http协议
  13. ASP.NET MVC 3和Razor中的@helper
  14. SoapUI接口测试实战
  15. JAVA实现QQ登录、注册等功能
  16. 近几年微软笔试题汇总分类解析
  17. 分享一款国产并口PSRAM存储芯片EMI164NA16LM
  18. 【论文笔记】AAAI2022多智能体强化学习论文五篇
  19. 第002篇 深入体验C#项目开发(一)
  20. Java后端Cookie工具类(设置Cookie有效时间、得到Cookie的域名等方法)

热门文章

  1. Excel表格数据不能编辑?
  2. excel——处理日期格式问题
  3. 光伏“就地消纳”的新推手:多能互补
  4. Web应用界面权限控制要点总结
  5. 百度蜘蛛最新UA及各大搜索引擎蜘蛛爬虫UA汇总
  6. 计算机术语 抖动,如果计算机屏幕闪烁和抖动,该怎么办
  7. Android 仿芝麻信用进度条,自定义View仿支付宝芝麻信用分仪表盘效果
  8. 独立GPSamp;50米防水 华为运动手环上手体验
  9. swc文件数据下载的地方-neuromorpho
  10. 从破解某设计网站谈前端水印(详细教程)