C++中函数名或&func均可获取函数真实入口地址,但必须严格匹配函数类型;内联/模板/虚函数及成员函数有特殊限制,动态获取需处理name mangling和符号可见性。

函数名直接取地址就是入口地址,但必须注意类型匹配
在 C++ 中,&func 或 func(在需要函数指针的上下文中)得到的就是该函数的内存入口地址,不是“符号名”,也不是“跳转表偏移”,而是真实加载后可执行的机器码起始位置。但关键陷阱在于:C++ 严格区分函数类型,void(*)() 和 int(*)(int) 是完全不同的类型,强制 reinterpret_cast 可能绕过检查,但会破坏 ABI 兼容性或触发未定义行为。
- 普通非虚、非模板、非内联函数:取地址安全,地址稳定(PIE 程序中为加载基址 + 偏移,运行时确定)
- 内联函数:编译器可能不生成独立符号,
&func编译失败或报undefined reference - 模板函数实例化体:每个实例有独立地址,
&foo和&foo地址不同 - 类成员函数:不能直接取地址为
void*,必须用decltype(&T::func)保存,其底层可能是 thunk 或带隐式 this 参数的调整地址
用 dlsym 或 GetProcAddress 获取动态符号地址要先确保符号未被隐藏
如果函数定义在共享库(.so / .dll)中,且你想在运行时通过名字查地址,得靠动态链接器。但默认情况下,C++ 编译器会对函数名做 name mangling,dlsym(handle, "my_func") 会失败——你得传 mangled 名,或者用 extern "C" 导出。
- 加
extern "C"后,my_func在符号表中就是 plain name,dlsym(handle, "my_func")返回真实入口地址 - 不加
extern "C"时,可用nm -C libxxx.so | grep my_func查 mangled 名(如_Z7my_funcv),再传给dlsym - Linux 下若编译时加了
-fvisibility=hidden(常见于库项目),即使extern "C"也需额外加__attribute__((visibility("default"))) - Windows 上
GetProcAddress同理,但 DLL 需显式__declspec(dllexport),否则符号根本不出现在导出表
用 objdump 或 readelf 静态确认地址时,注意节区与重定位
想脱离运行环境看函数地址?objdump -t 显示的是链接视图(.text 节内的偏移),而 objdump -d 反汇编显示的是重定位后(或 PIE 关闭时)的虚拟地址。两者不一定相等——尤其对 PIE 可执行文件,加载地址是随机的。
-
objdump -t a.out | grep my_func→ 输出类似00000000000011a0 g F .text 0000000000000012 my_func,其中11a0是相对于段起始的偏移,不是绝对 VA -
objdump -d a.out | grep -A5 "→ 显示反汇编指令,地址列是链接时假设的 VMA(如:" 00000000000011a0),实际运行时可能被 ASLR 偏移 - 真正运行时地址 =
/proc/PID/maps中对应 .text 段起始地址 + 静态偏移(例如11a0) -
readelf -s更可靠,它列出所有符号及其绑定(GLOBAL/WEAK)、类型(FUNC)、值(即节内偏移)
调试器里打印函数地址最直观,但要注意符号解析时机
GDB 中 p &my_func 或 info address my_func 显示的地址,是当前调试进程的实际运行地址(已受 ASLR 影响)。但前提是该函数符号在 debug info 中存在且未被优化掉。
- 编译必须带
-g,否则 GDB 不知道符号名到地址的映射 - 加
-O2后,若函数被内联或优化删除,GDB 可能提示Symbol "my_func" not found -
disassemble my_func不仅显示地址,还会列出指令字节和对应源码行(若有),是验证入口地址最直接的方式 - LLDB 类似:
image lookup -n my_func或target variable &my_func
C++ 函数指针地址本身很简单,难的是跨工具链、跨构建选项、跨运行时环境的一致性判断——mangling、visibility、PIE、ASLR、debug info 存在与否,任何一个环节断掉,你拿到的就不是你想要的那个“入口”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











