C++动态加载库函数:名称查找与重载函数加载疑问
按需加载共享库中的函数:处理名字修饰与重载函数
关于_ZN5Foo3barEv这类名字的由来
你看到的_ZN5Foo3barEv是C编译器的**名字修饰(Name Mangling)**结果。因为C支持重载、类成员函数、命名空间等特性,编译器会把原函数名转换成这种唯一的字符串,确保不同重载、不同类的同名函数在符号表中不会冲突。比如Foo::bar()这个无参成员函数,经过GCC系列编译器修饰后就会变成_ZN5Foo3barEv。
如何获取正确的修饰后函数名
有几种实用方法可以拿到目标函数的修饰后名字:
- 用
nm工具查看共享库的符号表:执行nm -D your_library.so | grep bar,会列出所有包含bar的符号,其中就有各个重载版本的修饰后名字。 - 用
c++filt工具反向解析:如果拿到了修饰后的名字,想确认对应的原函数名,执行c++filt _ZN5Foo3barEv,会输出Foo::bar();反过来,如果你知道原函数名,也可以通过编译测试代码并查看符号表的方式得到修饰名。 - 编译时保留中间文件:编译库代码时添加
-save-temps参数,生成的.s汇编文件里会直接显示函数的修饰后名字。
加载特定的重载函数
每个重载函数的修饰后名字都是唯一的,只要找到对应重载版本的修饰名,用dlopen+dlsym加载即可。举个实际例子:
假设你的共享库中有以下重载函数:
class Foo { public: void bar(); void bar(int x); };
它们的修饰后名字分别是_ZN5Foo3barEv(无参版本)和_ZN5Foo3barEi(int参数版本)。加载代码可以这么写:
#include <dlfcn.h> int main() { void* lib_handle = dlopen("./your_lib.so", RTLD_LAZY); if (!lib_handle) { // 处理加载失败 return 1; } // 加载无参的Foo::bar() void (*foo_bar)(Foo*) = reinterpret_cast<void (*)(Foo*)>(dlsym(lib_handle, "_ZN5Foo3barEv")); // 加载带int参数的Foo::bar(int) void (*foo_bar_int)(Foo*, int) = reinterpret_cast<void (*)(Foo*, int)>(dlsym(lib_handle, "_ZN5Foo3barEi")); // 使用函数(需要确保Foo类的定义在当前代码中可见) Foo obj; if (foo_bar) foo_bar(&obj); if (foo_bar_int) foo_bar_int(&obj, 42); dlclose(lib_handle); return 0; }
注意:类成员函数的隐式第一个参数是this指针,所以在声明函数指针时要把这个指针类型加上。
额外提示
如果不想处理名字修饰的问题,可以在共享库中用extern "C"声明函数,但这样会失去C++的重载特性——因为C语言不支持重载,编译器不会对extern "C"的函数进行名字修饰。
内容的提问来源于stack exchange,提问作者ThePenguin
相关产品推荐
相关产品推荐

