函数名本身就是函数入口地址,add与&add等价;声明指针可直接用auto fp = add;,传参也无需&;但static/inline函数可能无外部符号,动态库中须用dlsym/GetProcAddress获取地址。

函数名本身在 C++ 中就是函数的入口地址,不需要“获取”——直接用就行;所谓“强转”或“符号解析”通常发生在跨模块、动态加载或逆向调试场景,不是日常编码需要的操作。
函数名就是地址,add 和 &add 等价
在绝大多数编译器(GCC、Clang、MSVC)下,函数名天然退化为指向其入口的指针。写 add 或 &add 效果完全一样,类型都是 int (*)(int, int)(假设 add 是个二元整型加法函数)。
常见错误现象:
- 误以为
add是函数调用,结果在需要指针的地方写了add(),编译报错error: called object is not a function - 对
&add加一层取址,写成&&add,触发非法操作(函数不可取地址的地址)
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 声明函数指针时直接赋值:
auto fp = add;或int (*fp)(int, int) = add; - 传参给回调函数时也直接传名:
register_callback(add);,无需加& - 若函数是
static或 inline,可能被优化掉或无外部符号,此时&add仍合法,但动态链接时无法从符号表找到它
reinterpret_cast 强转函数指针到 void* 是未定义行为
C++ 标准明确禁止将函数指针隐式或显式转为 void*。虽然 GCC/Clang 在 x86-64 上常允许 reinterpret_cast<void>(add)</void> 编译通过,但这不保证可移植,且在 ARM64 或某些嵌入式平台会失败甚至崩溃。
真正安全的跨类型转换只有一种路径:
- 先转成同尺寸整型(如
uintptr_t):uintptr_t addr = reinterpret_cast<uintptr_t>(add);</uintptr_t> - 再转回函数指针时必须用原类型:
auto fp = reinterpret_cast<int int>(addr);</int> - 不能跳过中间整型,直接
reinterpret_cast<void>(add)</void>→reinterpret_cast<int>(...)</int>
性能与兼容性影响:
- 整型中转不产生运行时代价,只是位拷贝
- Windows 的
GetProcAddress返回的是FARPROC(本质是void*),必须用reinterpret_cast转回具体函数类型才能调用,这是平台 API 的妥协,不是语言推荐做法
动态库中获取函数地址要用 dlsym / GetProcAddress,不是靠 &
当函数在 .so 或 .dll 中,且未在当前翻译单元声明时,你无法直接写 &func_name —— 编译器根本不知道这个符号存在。
正确做法是:
- Linux 下用
dlsym(RTLD_DEFAULT, "func_name"),返回void*,再强转为具体函数指针类型 - Windows 下用
GetProcAddress(hModule, "func_name"),同样返回HMODULE下的FARPROC - 必须确保函数导出:Linux 加
__attribute__((visibility("default"))),Windows 加__declspec(dllexport)
容易踩的坑:
- 函数名发生 C++ name mangling,
dlsym查不到。解决方法:用extern "C"包裹声明,或用nm -D libxxx.so | c++filt查真实符号名 - 忘记检查返回值是否为
nullptr,导致后续调用崩溃 - 类型强转写错,比如把
int(*)(float)错写成int(*)(int),调用时栈帧错乱
调试时看函数入口地址,别信 IDE 的“跳转到定义”
IDE(如 VS、CLion)显示的“地址”往往是符号地址(linker script 中的虚拟地址),不是运行时真实加载地址。真要看运行时入口,得用:
- GDB:
p &func_name或info address func_name - LLDB:
image lookup -n func_name - 程序内打印:
printf("addr: %p\n", (void*)func_name);(注意:这里%p要求void*,所以必须转)
关键提醒:函数指针的“地址”不是数据地址,它指向代码段(.text),该内存页默认不可写。任何试图通过函数指针修改指令的行为(如 patching)需先调用 mprotect 或 VirtualProtect 改写权限,否则触发 SEGFAULT 或 ACCESS_VIOLATION。


















