唯一标准允许获取C++成员函数机器码地址的方法是用memcpy提取成员指针二进制内容并按ABI解析;虚函数需通过vtable索引获取;无捕获lambda转函数指针是最安全替代方案。

你需要在调试、Hook 或跨语言绑定场景中拿到 C++ 类成员函数实际执行的机器码起始地址,而不是一个类型安全但无法传给底层系统(如 Lua C API、Windows SetWindowsHookEx、Linux ptrace)的成员指针;直接 reinterpret_cast
用 memcpy 提取原始字节并解析函数地址字段
这是唯一被标准允许、跨平台可读取成员函数指针二进制内容的方式。union 访问在 C++20 后受严格限制,memcpy 是规避 strict aliasing 的合法手段。
第一步:声明成员函数指针变量,并确保目标函数为非虚、无多重继承、无虚基类。
第二步:分配足够容纳该指针字节长度的缓冲区,例如 GCC/Clang x86_64 下通常为 16 字节,MSVC x64 下为 8 字节。使用 std::memcpy 将指针对象完整拷贝到 uint8_t 数组中。
立即学习“C++免费学习笔记(深入)”;
第三步:根据目标平台 ABI 解析字段。对 Itanium ABI(GCC/Clang),后 8 字节(索引 8~15)常为函数真实入口地址;对 MSVC x64,前 8 字节即为地址字段(仅当无 this 调整时成立)。
第四步:将解析出的字节序列 reinterpret_cast 为 void*。注意:【该地址不可直接 call,必须配合有效 this 指针和正确调用约定(如 thiscall)才能执行】
通过虚函数表定位虚成员函数真实入口
虚函数没有统一的“&Class::func”裸地址,其真实跳转目标始终藏在对象实例的 vtable 中。此法不依赖成员指针本身,只依赖对象内存布局,且适用于所有虚函数。
方法一:手动读取 vtable 首地址并索引
创建已完全构造的对象实例,确保其 vtable 已就绪(避开构造函数中途调用风险)。
用 reinterpret_cast
根据虚函数在类中声明顺序(从 0 开始计数)读取对应槽位,例如第一个虚函数取 vtable[0],第二个取 vtable[1]。
方法二:用 __builtin_return_address(0) 辅助验证(仅限 GCC/Clang Debug)
在成员函数内部插入 __builtin_return_address(0),它返回的是该函数被调用时的返回地址——减去 call 指令长度(x64 为 5 字节)后,可逼近函数起始地址。但 【Release 模式下该值不可靠,编译器可能内联或优化掉该调用】
包装为无捕获 lambda 获取标准函数指针
这是最安全、最符合现代 C++ 实践的替代方案。无捕获 lambda 可隐式转换为函数指针,该指针是标准允许的 void* 可转类型,且语义明确。
写一个以对象引用为参数、调用目标成员函数的 lambda:[&obj]() { obj.func(); }。
用 decltype 推导其类型,再用 static_cast 转为函数指针类型,例如 auto fn = static_cast
该函数指针可安全 reinterpret_cast
这一步操作起来很简单,直接把 lambda 写进回调注册函数参数里就行。


















