c++oding="utf-8" ?>
直接取函数地址得到的是该函数在内存中的真实可执行入口虚拟地址(va),而非修饰后的符号名;需配合dladdr等工具获取符号名后,再用__cxa_demangle反解。

直接取函数地址会得到什么?
C++ 函数名在编译后会被 mangling(符号修饰),比如 void foo(int) 可能变成 _Z3fooi。但你用 &foo 获取的地址,不是“被修饰后的符号地址”,而是该函数在内存中真实的可执行入口地址——只要函数不是 inline、not defined 或虚函数未绑定,&foo 就是有效的虚拟地址(VA),且与链接器最终布局一致。
- 普通非虚、非模板具现化函数:直接取地址安全,
&foo返回的就是运行时跳转用的 VA - 模板函数或内联函数:必须显式具现化(如声明
template void foo<int>(int);</int>并定义),否则&foo<int></int>可能链接失败或返回 nullptr - 类成员函数(非虚):需用
&MyClass::foo,得到的是“带 this 调用约定”的地址,不能直接当 C 函数指针用 - 虚函数:不能直接取
&obj.foo,必须通过对象实例 + vtable 偏移计算,或用reinterpret_cast提取 vptr 后查表
用 abi::__cxa_demangle 反解符号名
abi::__cxa_demangle 是 libstdc++ 提供的标准 Demangle 接口,它只处理 mangled 符号字符串(如 _Z3fooi),不接受函数地址。所以你要先拿到符号名,再传给它。
- 先用
dladdr(Linux/macOS)从地址反查符号名:struct Dl_info info;<br>if (dladdr((void<em>)&foo, &info) && info.dli_sname) {<br> char</em> demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr);<br> if (demangled) { /<em> 使用 demangled </em>/ free(demangled); }<br>} - Windows 下用
DbgHelp的SymFromAddr+UnDecorateSymbolName,注意要初始化符号引擎并加载 PDB -
abi::__cxa_demangle第三个参数(size_t<em></em>)若非 nullptr,会写入输出缓冲区大小;第四个参数(int)用于返回错误码(0=成功,-1=无效 mangled 名,-2=内存不足) - 不要对任意字符串调用它——未 mangling 的字符串(如 "printf")会导致返回 nullptr,且不修改输出缓冲区
为什么 __cxa_demangle 有时返回空?
常见原因不是 API 用错,而是输入根本不是有效 mangled 名:
- 传了函数地址本身(如
__cxa_demangle((char*)&foo, ...))→ 错误,地址不是字符串 -
dladdr返回的dli_sname为 nullptr(例如函数被 -fvisibility=hidden、strip 掉、或在无符号的静态库中) - 函数是编译器生成的辅助符号(如
__cxx_global_var_init),虽被 mangling 但不在标准 ABI 规则内 - 使用了 Clang 的
-fno-rtti -fno-exceptions,部分 libstdc++ Demangle 实现可能被裁剪,建议改用 libc++ 的__cxa_demangle或直接调用cxxabi.h头
Windows 上获取真实 VA 和 Demangle 的实操差异
MSVC 默认不生成 GNU-style mangling,它用自己的修饰规则(如 ?foo@@YAXH@Z),所以不能直接喂给 abi::__cxa_demangle:
- 获取 VA 一样可靠:
auto ptr = &foo;在 x64 下就是真实 VA(ASLR 开启时是基址+偏移,仍可直接 call) - Demangle 必须用 Windows SDK 的
UnDecorateSymbolName,且传入标志UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS才干净 - 如果用 MinGW-w64 编译,则可能走 libstdc++ 路线,此时
__cxa_demangle可用,但需确认目标平台 ABI(COFF vs. ELF)和链接器行为 - 注意:/GL(全程序优化)可能内联或丢弃函数,导致地址不可取或
dladdr/SymFromAddr 查不到符号
调试符号缺失、函数被优化掉、跨编译单元 inline 冲突——这些比 Demangle 技术细节更常导致“地址拿不到”或“名字还原失败”。实际工程中,与其反复调试反解逻辑,不如在构建时保留 -g + -frecord-gcc-switches,并用 nm -C 或 llvm-nm -C 静态验证符号是否存在。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











