在C++中,函数名func与&func等价,均表示函数指针;但禁止将函数指针reinterpret_cast为void*,属未定义行为,应使用平台特定API如dlsym或GetProcAddress。

函数名直接取地址就是函数指针值
在 C++ 中,func 和 &func 在绝大多数上下文中是等价的,都表示指向函数的指针。编译器会自动将函数名隐式转换为函数指针类型,所以不需要强制加 & —— 但加了也不错,语义更明确。
常见误区是以为必须写 &func 才算“取地址”,其实不加也一样;反过来,对已经是指针类型的变量(比如 auto ptr = &func;),再写 &ptr 就错了,那得到的是指向函数指针的指针(int (**)(int)),不是你想要的。
- 正确:
void (*p1)(int) = func;、void (*p2)(int) = &func; - 错误:
void (**p3)(int) = &&func;(语法非法)或auto p = &func; auto bad = &p;(类型错) - 注意:函数模板实例化后的名字不能直接用作左值,
&foo<int></int>合法,但foo<int></int>单独出现可能触发 SFINAE 或解析歧义
用 reinterpret_cast 转成 void* 需谨慎
标准 C++ 明确禁止将函数指针隐式或显式转为 void*(ISO/IEC 14882 §7.6.1.9)。虽然很多平台(x86-64 Linux/macOS)上 reinterpret_cast<void>(func)</void> 能编译且运行正常,但这属于未定义行为(UB),在嵌入式、某些 Windows ABI 或 future 标准中可能失效。
如果你真需要把函数地址当数据处理(比如写 JIT、打 patch、日志记录),应优先考虑平台特定方式:
立即学习“C++免费学习笔记(深入)”;
- POSIX:
dlsym(RTLD_DEFAULT, "func_name")返回void*,但这是符号查找,不是取地址 - Windows:
GetProcAddress(hMod, "func_name")同理 - 调试信息可用
addr2line或 DWARF 解析,但和运行时指针值不是一回事
别为了“打印地址”硬转 void*——用 printf("%p", (void*)(uintptr_t)func) 是常见 hack,但本质仍是 UB;更稳妥的是用 std::cout (func),部分标准库实现允许该转型(非标准保证)。
lambda 和成员函数不能直接取地址
普通函数可以,但 auto f = []{}; 这样的 lambda 表达式生成的是闭包类型对象,它没有函数指针类型,&f 是对象地址,f 本身也不是函数名,不能隐式转成函数指针(除非无捕获且显式转换为函数指针类型)。
- 无捕获 lambda 可转:
auto l = []{ return 42; }; int(*fp)() = l;✅ - 有捕获 lambda 不可转:
[x=1]{};→ 编译失败 ❌ - 非静态成员函数必须绑定对象:
&MyClass::method得到的是成员函数指针(类型如int (MyClass::*)()),不是普通函数指针;要调用需配合对象实例,不能直接当void(*)()用
调试时查看函数指针值的实际方法
想确认某个函数指针变量存的到底是什么地址?别只信 std::cout —— 流输出可能被重载或格式化异常。最可靠的方式是用调试器直接看内存值:
- GDB:
p/x &func或p/x ptr(ptr是函数指针变量) - LLDB:
expr -f x -- &func - Clang/GCC 编译时加
-g,确保符号未被 strip
注意:内联函数、static 函数、模板函数实例化体可能被优化掉,导致地址不可见或与预期不符;加 __attribute__((noinline)) 或 [[gnu::noinline]] 可强制保留符号。


















