在 C 语言里,对函数名取地址其实没有实际意义:函数名本身就是一个地址值,`f` 和 `&f` 求得的结果完全相同。更有意思的是,大多数编译器对函数的解引用也做了妥协处理,于是下面这段看起来很离谱的代码竟然可以正常编译运行。

#include <stdio.h>


void (*pf)(void);
void f(void)
{
	printf("1\n");
}

int main(int argc, char *argv[])
{
   pf = &f; // 没问题
   pf = ***f; // 取址?
   pf(); // 函数指针可以调用?
   (****pf)();  // 这又是什么?
   (***********************************************f)(); // 这个够变态了吧?

	return 0;
}

几个值得注意的地方:

  • pf = &fpf = f 是等价的,都把函数 f 的地址赋给了函数指针 pf
  • pf = ***f 连续多次解引用看似荒谬,但编译器会把每一次解引用再退化回函数指针,结果仍然是同一个函数入口。
  • pf()(****pf)() 都能正常调用函数,无论你在前面套多少个 *,最终求出的还是那个可调用实体。
  • 极端情况 (***********************************************f)() 也能运行,这就是编译器对函数与函数指针之间互相转换做的放宽规则。

换句话说,函数和函数指针在绝大多数上下文里是可以相互转换的:函数名会自动退化为函数指针,而对函数指针做任意次解引用又会重新退化回函数指针。这也是为什么上面那段"变态"的写法在语法层面并不会报错。