函数指针变量本身不等于函数入口地址,&func 和 func 数值通常相同但语义不同;C++标准不保证其可安全转为void*,成员函数指针更非裸地址,推荐用dladdr(Linux)或DbgHelp(Windows)进行符号解析而非强转。

函数指针变量本身不等于函数入口地址,&func 和 func 在多数场景下值相同但语义不同
直接取函数名(如 func)或对函数名取地址(&func)在 C++ 中通常得到相同的数值——即该函数第一条指令的内存地址。但这不是“强制保证”,而是编译器对普通非虚、非模板、非内联函数的常见实现。关键在于:C++ 标准只规定函数指针可隐式转换为 void* 是**条件性支持**(C++17 起明确为未定义行为),不能直接用 reinterpret_cast<void>(func)</void> 安全获取地址。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 若目标是调试、符号比对或与底层系统(如
dlsym、Windows 的GetProcAddress)交互,优先走符号表解析路径,而非强转函数指针 - 对普通函数,可用
printf("%p", static_cast<void>(reinterpret_cast<void>(func)));</void></void>—— 但仅限于你完全控制编译器、ABI 和运行环境(如 Linux x86_64 + GCC),且函数未被优化掉 - 一旦函数是 inline、模板实例化、成员函数或虚函数,
func不再是简单地址,强转会失效甚至崩溃
成员函数指针不能直接转 void*,必须用 std::memcpy 拆解或依赖 ABI 特定布局
C++ 成员函数指针不是裸地址,它可能包含 this 偏移、vtbl 索引、甚至多个字段(如 Itanium ABI 下 16 字节)。直接 reinterpret_cast<void>(&A::foo)</void> 取到的只是结构体首字节地址,不代表可执行代码位置。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 非静态成员函数真正可执行的地址,只在其被调用时、绑定具体对象后才确定(尤其是虚函数)
- 若硬要提取非虚成员函数的“代码地址”,可用
std::memcpy把成员指针按平台字节数拷贝到uintptr_t数组,再分析(例如 GCC x86_64 下前 8 字节常为偏移或地址)——但这是 ABI 依赖行为,不可移植 - 更安全的做法:把成员函数包装成 lambda 或静态桥接函数,再取其地址,例如
auto wrapper = [&obj]() { obj.foo(); }; auto ptr = +[](auto&& f) -> void(*)() { return reinterpret_cast<void>(f); }(wrapper);</void>
dladdr 是 Linux 下反查函数符号与地址最可靠的运行时手段
比起猜测和强转,Linux 提供了 dladdr(需链接 -ldl)从任意内存地址反推所属符号名和共享对象路径,这才是生产环境定位函数真实入口的正解。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 先确保函数未被内联:
__attribute__((noinline))或[[gnu::noinline]] - 获取地址时用
reinterpret_cast<uintptr_t>(func)</uintptr_t>转整数,再传给dladdr;不要用void*直接传,避免指针截断 -
dladdr返回的dli_sname可能为空(如静态函数、未导出符号),此时需配合nm -D或objdump -t静态分析 - 注意:
dladdr对 main 可执行文件中符号也有效,但要求编译时加-rdynamic(等价于--export-dynamic)
Windows 下用 DbgHelp 解析符号,GetProcAddress 只适用于 DLL 导出函数
Windows 没有等价于 dladdr 的轻量 API。GetProcAddress 只能查 DLL 显式导出的函数(即带 __declspec(dllexport) 或 DEF 文件声明),对 EXE 内部函数或静态库函数无效。真要查地址对应符号,得上 DbgHelp。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 初始化必须调用
SymInitialize(GetCurrentProcess(), nullptr, TRUE),否则后续失败静默 - 查地址前先用
SymFromAddr,传入reinterpret_cast<dword64>(func)</dword64>(注意 64 位用DWORD64,不是DWORD) - 返回的
SYMBOL_INFO结构需自行分配足够空间(sizeof(SYMBOL_INFO) + 256),否则缓冲区溢出 - 若
SymFromAddr失败,大概率是 PDB 未加载或路径不对;可用SymSetSearchPath手动指定 PDB 路径
函数地址的“真实值”在不同上下文里含义完全不同:链接视图、加载视图、调试视图、JIT 视图……别把调试器里看到的地址当成能随便 memcpy 的东西。符号解析永远比裸地址强转靠谱,尤其当代码要跑在不同优化等级或不同平台时。


















