&func 获取的地址不总是函数真实入口,受内联、lto、aslr、链接选项等影响;跨平台反解函数名须用 abi::__cxa_demangle,linux 下推荐 dladdr 配合使用。

直接取 &func 得到的不一定是你调试器里看到的那个地址,更不保证能反解出可读函数名——这取决于编译器是否内联、是否启用 LTO、有没有 extern "C"、是否带调试信息,以及你用的是什么平台。
普通函数取址:&func 为什么有时不准
对非 inline、非 static、非模板的全局函数,&func 在多数情况下确实返回其 .text 段起始地址。但以下情况会让它失效或误导:
- MSVC 启用增量链接(
/INCREMENTAL)时,func可能指向一个跳转桩(thunk),真实代码在别处;禁用该选项后地址才稳定 - PIE 或 ASLR 程序中,
&func返回的是运行时重定位后的地址,而objdump -t查到的是文件偏移,两者数值不同但差值固定 - 函数被 LTO 全局优化删除,或仅作为内联候选未生成独立符号,此时
&func编译失败或链接时报undefined reference -
reinterpret_cast<uintptr_t>(&func)</uintptr_t>是常见写法,但 C++17 起将函数指针转void*明确列为未定义行为,仅 GCC/Clang x86_64 在特定 ABI 下“碰巧”可行
运行时 Demangle:必须用 abi::__cxa_demangle,不是 core::demangled_name
core::demangled_name 不存在于任何标准实现或主流 ABI 中,是常见误传。唯一跨平台可用的接口是 abi::__cxa_demangle,声明于 <cxxabi.h></cxxabi.h>:
- 输入必须是 mangled 名字符串,比如从
typeid(Obj).name()拿不到函数名——typeid对函数类型无效;更可靠来源是dladdr+ 符号表,或异常what()中提取 - 调用后需检查
status:只有值为0才表示成功;-1(非法 mangled 名)、-2(内存不足)都意味着失败 - 返回值是堆分配的
char*,必须手动free(),否则泄漏;不能直接std::string构造后不管 - Windows 不提供该函数,得换用
UnDecorateSymbolName(dbghelp.h),且需传入 MSVC 风格的 decorated 名(如?my_func@@YAXXZ)
Linux 下最稳的组合:dladdr + __cxa_demangle
比起猜 &func 地址或硬解析符号表,dladdr 是 Linux 生产环境定位函数真实入口的首选方法:
- 先确保函数未被内联:
[[gnu::noinline]]或__attribute__((noinline)) - 调用
dladdr(reinterpret_cast<void>(ptr), &info)</void>,其中ptr是已知有效函数指针(如&my_func) -
info.dli_sname给出 mangled 名(如_Z7my_funcv),info.dli_saddr是实际加载地址(即你真正要的入口) - 再把
info.dli_sname传给abi::__cxa_demangle,得到可读名;注意:若dli_sname == nullptr,说明符号被-fvisibility=hidden隐藏了 - 需链接
-ldl,且二进制必须保留符号信息(未 strip,或至少含.dynsym)
成员函数地址和 Demangle 是两回事
非静态成员函数指针(如 void (MyClass::*)())根本不是一个地址,而是编译器私有结构(MSVC x64 下常为 16 字节,含 function_address 和 this_adjustment)。强行 reinterpret_cast<void>(&MyClass::foo)</void>:
- 得到的是该结构体首地址,不是可执行代码地址
- 虚函数、多重继承下结构更复杂,字段含义随 ABI 变化,无法跨平台解释
- 即使你从 union 或 memcpy 中拆出某个字段(如 MSVC 的第一个 8 字节),它也只对非虚、单继承、无 this 调整的函数有意义
- Demangle 更麻烦:成员函数的 mangled 名包含类名、参数、const/volatile 限定符等,
abi::__cxa_demangle能解,但你得先拿到那个字符串——dladdr对成员函数指针基本无效,只能靠nm -C或 GDB 查
真正需要“真实地址”的场景(如 hook、JIT、热补丁),不要依赖 &func 的数值一致性;优先走调试符号路径(dladdr/SymFromName)或工具链辅助(addr2line + objdump),因为它们绕过了 ABI 实现细节,直击符号与调试信息本身。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











