C语言:函数名自指时&取址为何仍为函数地址及指针调用疑问
关于函数名、&函数名与函数指针调用的疑问解答
咱们一步一步拆解这两个C语言函数指针的经典“反直觉”问题:
为什么&f和f得到的都是函数地址?
首先得纠正一个误解:函数名本身不是“指向自身的指针”,它本质是一个函数类型的标识符。但在C语言的绝大多数表达式场景中,函数名会被编译器隐式转换为指向该函数的指针——这是C标准规定的自动转换,只有两种例外情况:
- 函数名作为
sizeof的操作数(不过sizeof(函数名)本身是非法的,因为函数没有“大小”这个属性) - 函数名作为
&运算符的操作数
当你写&f时,&作用于函数类型的标识符,得到的结果就是指向该函数的指针;而直接写f时,编译器会自动把函数类型转换成函数指针。这两种方式得到的指针值完全相同,因为函数的“地址”就是它的代码入口点——函数不是存储在栈/堆上的变量,不存在“函数指针的地址”这种东西,所以&f不会返回“指针的指针”,而是和f等价的函数指针。
你可以用代码验证这个结论:
#include <stdio.h> void f(int a) {} int main() { void (*p)(int) = f; void (*q)(int) = &f; // 打印两个指针的地址,结果完全一致 printf("p = %p\n", (void*)p); printf("q = %p\n", (void*)q); return 0; }
为什么(*p)(2)和p(2)都合法?
这依然是C语言隐式转换规则在起作用:
- 当你写
p(2)时,p是函数指针,函数调用运算符()可以直接接受函数指针作为操作数,编译器会直接通过这个指针找到函数入口执行。 - 当你写
(*p)(2)时,*p会把函数指针解引用,得到一个函数类型的表达式。但紧接着,在()运算符前,这个函数类型的表达式又会被编译器自动转换回函数指针——相当于绕了一圈又回到了p。
甚至你写(**p)(2)、(***p)(2)都是合法的,因为每次解引用后都会被自动转回去。这是C语言为了简化开发者操作的语法设计,不管你是直接用指针还是解引用后使用,编译器都能正确处理。
看个实际运行的例子:
#include <stdio.h> void f(int a) { printf("Called with a = %d\n", a); } int main() { void (*p)(int) = &f; p(2); // 正常调用 (*p)(2); // 解引用后调用,和上面效果一致 (**p)(2); // 多次解引用,依然正常运行 return 0; }
总结一下:这些都是C标准规定的隐式转换特性,目的是让函数指针的使用更灵活,不用写繁琐的冗余语法。
内容的提问来源于stack exchange,提问作者Zahra19977
相关产品推荐
相关产品推荐

