C与CPP混合工程函数方法互相调用的实现

C与CPP混合工程函数方法互相调用的实现

起因

需要在c和cpp的混合工程里相互调用函数方法,是在库中相互调用的,编译时都可以编译通过,但是运行加载时报错,提示无法找到符号表。

分析原因

  1. c 和 cpp 编译生成的可执行文件都是通过符号表来找到需要调用的方法。但是 cpp 在编译之后生成的符号表的内容有些特别,它会在符号中增加一些前缀和后缀来指示一些特殊含义。
  2. 下面演示cpp 编译之后的符号表内容。
#include 
using namespace std;
 
int cpp_print(void)
{
    cout << "Hello, World!";
    return  0;
}

int main() 
{
    cpp_print();
    return 0;
}

编译之后生成的文件用 nm 命令查看它的符号表可以看到:cpp_print的符号被添加了前缀 _Z9 后缀 v。

% nm  a.out |grep print 
000000000000120c t _GLOBAL__sub_I__Z9cpp_printv
0000000000001189 T _Z9cpp_printv
  1. 演示 c 编译出来的内容
#include 

int c_print (void)
{
    printf("Hello, World!\r\n");
    return  0;
}

int  main (int  argc, char **argv)
{
    c_print();
    return  0;
}

编译之后生成的文件用 nm 命令查看它的符号表可以看到:c_print的符号和源文件中的函数名一致。

% nm  a.out |grep print             
0000000000001149 T c_print
  1. cpp 特殊的原因
    C++允许多个不同参数类型的函数拥有一样的名字,就是所谓的函数重载;另外C++还在语言级别支持名称空间,即允许在不同的名称空间有多个同样名字的符号。为了实现上面的功能,大佬们引入一个术语叫做函数签名(Function Signature),函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息。函数签名用于识别不同的函数,就像签名用于识别不同的人一样,函数的名字只是函数签名的一部分。

解决问题

使用 extern "C" 解决问题。

在 C 文件调用 Cpp 中实现的方法的做法: 需要使用 extern “C” 将函数定义。

#include 
using namespace std;
 
extern "C"
int c_print(void)
{
    cout << "Hello, extern C\r\n";
    return 0;
}

int cpp_print(void)
{
    cout << "Hello, World!";
    return  0;
}

int main() 
{
    cpp_print();
    c_print();
    return 0;

}

编译之后生成的文件用 nm 命令查看它的符号表可以看到:使用 extern "C" 包含的函数生成的符号表没有额外增加前后缀,这样在 c 代码中只需要显式地声明这个函数 int c_print(void) 即可。

% nm  a.out |grep print
0000000000001189 T c_print
0000000000001233 t _GLOBAL__sub_I_c_print
00000000000011ab T _Z9cpp_printv

在 Cpp 文件调用 C 中实现的方法的做法: 需要使用 extern “C” 将函数声明,即可在下面使用该方法。

#include 
using namespace std;
 
extern "C" int c_print(void); //告诉编译器这是在c实现的方法,

int cpp_print(void)
{
    cout << "Hello, World!";
    return  0;
}

int main() 
{
    cpp_print();
    c_print();
    return 0;
}

你可能感兴趣的:(c语言,c++,开发语言)