由于历史原因以及不同开发人员的技术偏好C语言和C语言都有一些独有的非常有价值的项目因而两种语言的互操作充分利用前人造的轮子是一件非常有价值的事情。C代码调用C代码很简单只要分别在包含的C头文件的开头和结尾加上如下的两个块123#ifdef __cplusplusexternC{#endif和123#ifdef __cplusplus}#endif即可。然而为了支持类、重载等更加高级的特性在编译C代码时C符号会被修饰。我们dump Linux平台加密库 libcrypto 的符号表可以看到如下的内容123456789101112131415161718192021222324252627282930313233343536373839404142$ readelf -s /usr/lib/libcrypto.soSymbol table.dynsymcontains 9607 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 00000000001daa58 0 SECTION LOCAL DEFAULT 92: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND _ZTIiCXXABI_1.3 (2)3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_locationGLIBC_2.2.5 (3)4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt18uncaught_exceptionvGLIBCXX_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 getservbynameGLIBC_2.2.5 (6)7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bindGLIBC_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_chkGLIBC_2.11 (7)10: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND _ZTIhCXXABI_1.3 (2)11: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND _ZTVSt9basic_iosIcSt11char_traitsIcEEGLIBCXX_3.4 (4)12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND socketGLIBC_2.2.5 (6)13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt14basic_ifstreamIcSt11char_traitsIcEED1EvGLIBCXX_3.4 (4). . . . . .86: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSo5writeEPKclGLIBCXX_3.4 (4)87: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDmallocGLIBC_2.2.5 (6)88: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt9basic_iosIcSt11char_traitsIcEE4initEPSt15basic_streambufIcS1_EGLIBCXX_3.4 (4)89: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSi5seekgElSt12_Ios_SeekdirGLIBCXX_3.4 (4)90: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_key_deleteGLIBC_2.2.5 (3)91: 0000000000000000 0 FUNC GLOBAL DEFAULT UND shutdownGLIBC_2.2.5 (6)92: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt15set_new_handlerPFvvEGLIBCXX_3.4 (4)93: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_getspecificGLIBC_2.2.5 (3)94: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDstrcmpGLIBC_2.2.5 (6)95: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDstrtolGLIBC_2.2.5 (6)96: 0000000000000000 0 FUNC GLOBAL DEFAULT UND ioctlGLIBC_2.2.5 (6). . . . . .186: 00000000002c5a80 142 FUNC GLOBAL DEFAULT 12 _ZN8CryptoPP6xorbufEPhPKhS2_m187: 00000000002fd6d0 9 FUNC WEAK DEFAULT 12 _ZN8CryptoPP21InvertibleRSAFunction9BERDecodeERNS_22BufferedTransformationE188: 00000000001ea840 73 FUNC GLOBAL DEFAULT 12 _ZN8CryptoPP13Base64Decoder22GetDecodingLookupArrayEv189: 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_EEED0Ev190: 0000000000278b60 86 FUNC WEAK DEFAULT 12 _ZN8CryptoPP8Rijndael3DecD1Ev191: 00000000001fd1f0 2 FUNC WEAK DEFAULT 12 _ZN8CryptoPP23DefaultEncryptorWithMAC8FirstPutEPKh192: 000000000026a490 51 FUNC GLOBAL DEFAULT 12 _ZN8CryptoPP23FilterWithBufferedInputC2EPNS_22BufferedTransformationE193: 0000000000285180 6 FUNC WEAK DEFAULT 12 _ZNK8CryptoPP8GCM_Base6IVSizeEv194: 000000000032e830 510 FUNC WEAK DEFAULT 12 _ZN8CryptoPP18StandardReallocateItNS_20AllocatorWithCleanupItLb0EEEEENT0_7pointerERS3_PT_NS3_9size_typeES8_b195: 00000000002a1790 185 FUNC WEAK DEFAULT 12 _ZSt18uninitialized_copyISt15_Deque_iteratorIyRKyPS1_ES0_IyRyPyEET0_T_S9_S8_196: 0000000000355610 25 OBJECT WEAK DEFAULT 14 _ZTSN8CryptoPP11RSAFunctionE. . . . . .这与我们在源文件和头文件里看到的那些函数、类的声明定义都不一样。通过binutils的工具cfilt demangle这些符号可以让我们看到它们在代码里的样子1234$ cfilt _ZTSN8CryptoPP11RSAFunctionEtypeinfo nameforCryptoPP::RSAFunction$ cfilt _ZN8CryptoPP18StandardReallocateItNS_20AllocatorWithCleanupItLb0EEEEENT0_7pointerERS3_PT_NS3_9size_typeES8_bCryptoPP::AllocatorWithCleanupunsignedshort,false::pointer CryptoPP::StandardReallocateunsignedshort, CryptoPP::AllocatorWithCleanupunsignedshort,false (CryptoPP::AllocatorWithCleanupunsignedshort,false, unsignedshort*, CryptoPP::AllocatorWithCleanupunsignedshort,false::size_type, CryptoPP::AllocatorWithCleanupunsignedshort,false::size_type,bool)那到底有没有办法在C代码中调用C代码呢方法当然是有的而且还不止一种。通过extern “C”调用在 .cpp 文件中定义一个函数声明为extern C则该函数可以方便地在C代码中调用。由于该函数在 .cpp 文件中定义因而在该函数的实现中可以调用任意的C代码包括C函数创建C类等等。C头文件1234567891011#ifndef CPPFUNCTIONS_H_#define CPPFUNCTIONS_H_#ifdef __cplusplusintcpp_func(intinput);externC{#endifintc_func(intinput);#ifdef __cplusplus}#endif#endif /* CPPFUNCTIONS_H_ */C实现文件如下1234567#include CppFunctions.hintcpp_func(intinput) {return5;}intc_func(intinput) {returncpp_func(input);}在C代码里调用C函数123456#include stdio.h#include CppFunctions.hintmain(intargc,char**argv) {printf(%d\n, c_func(10));return0;}在C文件里定义的c_func函数就像一座桥一样连接了C代码的世界和C代码的世界。但 C 函数c_func的参数及返回值的类型自然是受到一定的限制的但在函数实现中可以适配要调用的C接口做一些适配。通过dlopen/dlsym调用借助于在 .cpp 文件中定义的C函数间接地调用C接口固然是能实现在 C 代码中调用C代码的目标然而还是有些麻烦。通过libdl提供的接口可以使我们的目标通过更简便的方式实现。为dlsym传入经过修饰的符号可以找到对应的函数的地址。通过如下命令将上面的CPPFunctions.cpp文件编译为一个动态链接库1$ gcc -shared -fPIC CPPFunctions.cpp -o libCppLibTest.so通过dlopen和dlsym找到对应的C函数并将其强制类型转换为适当类型的函数指针然后通过函数指针调用目标函数如123456789#include dlfcn.h#include stdio.hintmain(intargc,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));return0;}编译并执行上面的代码在我的机器上可以看到如下的输出12cpp_func 0x7f35727a8650cpp_func output 5总结以上就是这篇文章的全部内容了希望本文的的内容对大家的学习或者工作能带来一定的帮助