直接取函数地址得到的是链接器填充后的有效入口地址;但内联、模板、constexpr 函数不可取址,非静态成员函数取址得用成员指针,lambda需先转函数指针;反解名字须结合dladdr与__cxa_demangle。

直接取函数地址会得到什么?
C++ 函数名在编译后通常被修饰(mangled),比如 void foo(int) 可能变成 _Z3fooi。直接对函数名取地址,如 &foo,拿到的是符号解析后的实际入口地址——这没错,但要注意:它不是“函数指针转换前的原始值”,而是链接器填充后的有效地址;对于内联函数、模板实例、constexpr 函数,&foo 可能根本无法取址(编译报错 undefined reference 或 cannot take address of function)。
- 普通非内联、非静态成员函数可安全取址,结果是可执行代码段中的真实地址(如
0x55a123456789) - 静态成员函数同普通函数,可取址
- 非静态成员函数需绑定对象,
&Class::method得到的是成员函数指针,不是内存地址,其底层表示可能含偏移/this 调整逻辑,不能直接 reinterpret_cast 成void* - lambda(无捕获)可隐式转函数指针,
+[](int x){return x<em>2;}</em>或static_cast<int>)(int)>([](int x){return x*2;})</int>后再取址才有效
如何把地址转回可读函数名?用 __cxa_demangle
GCC/Clang 的 __cxa_demangle 是运行时反混淆的唯一标准方式,头文件为 <cstdlib></cstdlib>(不需要额外链接,但需确保 libstdc++ 或 libc++ 已加载)。它不接受地址,只接受 mangled 名字字符串——所以你得先通过符号表或调试信息获取名字,再传给它。
常见错误是试图对地址做逆向查表:addr2line 或 dladdr 才是干这事的:
-
dladdr(POSIX)可从地址查到所在 SO/DLL 中的符号名(mangled 状态),配合__cxa_demangle才完整:#include <dlfcn.h> #include <cstdlib> #include <iostream> </li></ul><p>char<em> demangle_symbol(void</em> addr) { Dl_info info; if (!dladdr(addr, &info) || !info.dli_sname) return nullptr; int status = 0; char* demangled = __cxa_demangle(info.dli_sname, nullptr, nullptr, &status); return (status == 0) ? demangled : nullptr; }</p>-
status != 0表示不是 C++ mangling(比如 C 函数、编译器内置函数),此时直接用info.dli_sname - 返回的
demangled是 malloc 分配的内存,调用方必须free(),否则泄漏
为什么 std::function 或函数指针不能直接 demangle?
std::function是类型擦除容器,内部可能存函数指针、lambda、绑定对象等,不携带符号名信息;函数指针(如int(*)(int))只是地址,和名字完全脱钩。运行时没有机制能把一个裸地址自动映射回编译期生成的 symbol name——除非你主动记录(比如注册表 +<strong>FUNCTION</strong>宏),或依赖调试信息(libdw/libbfd)。立即学习“C++免费学习笔记(深入)”;
- 模板函数实例化后每个特化都有独立 mangled 名,但地址相同(如
vector<int>::size()</int>和vector<double>::size()</double>地址不同),dladdr查到的是具体实例的名字 - LTO(Link Time Optimization)可能合并等价函数,导致多个 mangled 名指向同一地址,
dladdr返回哪个取决于符号表顺序,不可预测
Windows 上怎么搞?别用 __cxa_demangle
MSVC 不提供
__cxa_demangle,对应的是UnDecorateSymbolName(<windows.h></windows.h>+DbgHelp.lib),且只能处理 MSVC mangling(?func@ns@@YAHH@Z类型):- 必须先调用
SymInitialize初始化符号引擎,否则返回空串 - 输入需是完整 mangled 名(
dladdr在 Windows 不可用,得用SymFromAddr配合 PDB) - 若没加载 PDB 或调试信息被剥离,
SymFromAddr只能返回模块内偏移(modulename!+0x1234),无法还原函数名
真正跨平台可靠的路径只有一条:构建时保留调试信息(
-g//Zi),出问题时用addr2line(Linux/macOS)或dumpbin /symbols+cv2pdb(Windows)离线分析,而不是指望运行时 100% 还原可读名。函数地址本身是确定的,但名字映射高度依赖工具链、构建选项和运行环境;想靠一行代码“自动打印当前函数名”,
__PRETTY_FUNCTION__宏比运行时 demangle 更轻量也更可靠。 -


















