c语言调用c 方法,C语言代码中调用C++代码的方法示例
由于历史原因,以及不同开发人员的技术偏好,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++代码的方法示例相关推荐
- c语言代码中调用系统命令行.sh shell脚本,linux shell system传参
C语言代码中调用命令行: 1. 使用system(" 命令行 "); -- 执行完命令行后,会返回原先C代码的位置,继续执行. 2. 如果命令行中需要传参,使用 sprin ...
- Golang cgo:如何在Go代码中调用C语言代码?
如何在Go代码中调用C语言代码? Go语言是通过自带的一个叫CGO的工具来支持C语言函数调用,同时我们可以用Go语言导出C动态库接口给其它语言使用. 方式一.直接在 Go 代码中写入 C 代码 检查是 ...
- 【Unity3D】Android Studio 工程中使用 Java 代码调用 Unity 的 C# 脚本 ( Java 中调用 UnityPlayer#UnitySendMessage 方法 )
文章目录 一. Java 调用 C# 依赖库准备 1.依赖库位置 2.unityLibrary 依赖库位置 二. Java 调用 C# 的 UnityPlayer#UnitySendMessage 方 ...
- 在python代码中调用vba宏的四种方法
在python代码中调用vba宏 工作以python为主体,但是遇到了一些word操作的需求(详见上一篇),这个需求用word自带的功能会很容易实现,于是就想着能不能用python调用宏来处理. 网上 ...
- 调用c++_WebAssembly: 在C代码中调用JS的函数
0. 前提知识点 导出C中的函数给JS调用:主要是EMSCRIPTEN_KEEPALIVE这个Emscripten环境特有的宏. #include <stdio.h>#ifndef EM_ ...
- vue怎么调用子元素的方法_vue 父组件中调用子组件函数的方法
vue 父组件中调用子组件函数的方法 在父组件中调用子组件的方法: 1.给子组件定义一个ref属性.eg:ref="childItem" 2.在子组件的methods中声明一个函数 ...
- 为什么 wait 方法要在 synchronized 中调用?
作者:Yujiaao 来源:https://segmentfault.com/a/1190000019962661 一个有难度的 Java 问题,wait 和 notify. 它们是在有 synchr ...
- 安卓代码中常用的代码以及问题收集
EnglishVersion ->_->:https://raw.githubusercontent.com/jiang111/awesome-android-tips/master/RE ...
- 实验四:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
贺邦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验目的: 使用库函数 ...
最新文章
- puppet(1.7-2.1)
- wordcount代码_Scala小案例之wordcount
- PostgreSQL操作问题(转载)
- Linux ENSP 搭建DHCP服务器并实现中继和Linux 下搭建DNS服务器(2个实验可跟做)
- 约瑟夫环 猴子选大王的问题
- java 图形绘制_Java Graphics 图形绘制
- 算法--排序--大小写字母数字分离(桶排序思想)
- mxnet深度学习(Symbol)
- aspell_如何使用Aspell在Linux命令行上检查拼写
- 信息必填的php,php - {“成功”:0,“消息”:“必填字段丢失”}在本地主机上测试文件 - 堆栈内存溢出...
- DAY8-Nessus漏洞扫描
- day9http协议
- ASP.NET MVC 3和Razor中的@helper
- SoapUI接口测试实战
- JAVA实现QQ登录、注册等功能
- 近几年微软笔试题汇总分类解析
- 分享一款国产并口PSRAM存储芯片EMI164NA16LM
- 【论文笔记】AAAI2022多智能体强化学习论文五篇
- 第002篇 深入体验C#项目开发(一)
- Java后端Cookie工具类(设置Cookie有效时间、得到Cookie的域名等方法)