我是一个C语言新手,发现下面两个对函数指针的调用都能很好地编译和工作,这很有趣。一个在调用函数指针之前取消引用,另一个没有取消引用。
#include <stdio.h>
#include <stdlib.h>
void func() {
puts("I'm a func");
}
int main(void) {
void (*f)() = func;
f();
(*f)();
return EXIT_SUCCESS;
}
我想我明白了(*f)()
是调用函数指针的“官方”方式,但为什么简单地调用f()
就可以工作呢?这是最近C版本的语法糖吗?
4条答案
按热度按时间kiayqfof1#
这是一块语法/语义糖,AFAIK,从最早的C语言版本起就开始工作了。如果你把函数看作指向代码的指针,这是有意义的。
使函数指针以这种方式工作所需的唯一特殊规则是,间接指向函数指针会返回相同的指针(因为无论如何都不能操作标准C中的代码):当
f
是函数指针时,则f == (*f) == (**f)
,等等。(旁白:要注意像
void (*f)()
这样的声明。空参数列表表示仅在返回类型上匹配的旧式C89之前的函数声明。对于类型安全,首选void (*f)(void)
。)db2dz4w82#
函数调用表达式的形式总是“函数指针”、“圆括号”、“参数”、“圆括号”。为了让你不必每次都拼出
(&printf)("Hello World\n")
,有一个单独的规则,表示函数的表达式衰减到相应的函数指针。由于函数指针可以被解引用以给予再次表示函数的表达式,因此这将再次衰减,因此您可以继续解引用,并且会有很多衰减:
1)早期的Perl有这样的函数语法。
0qx6xfy63#
在C中,你可以像这样调用函数:
都是有效的。在C中,解引用或获取函数的地址只是计算为指向该函数的指针,而解引用函数指针只是计算回函数指针。C的设计方式是函数名标识符和变量持有函数指针的含义相同:CODE存储器地址。它允许通过在标识符或变量上使用call
()
语法跳转到该内存。最后,但也是最不重要的,标准说:
C11:6.3.2.1:
4函数指示符是具有函数类型的表达式。除非它是
sizeof
运算符、_Alignof
运算符、65)或一元&
运算符的操作数,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回类型的指针”的表达式。mccptt674#
C 2011标准在条款6.3.2.1第4段中规定:
除非它是sizeof运算符、_Alignof运算符或一元**&**运算符的操作数,否则类型为“function returning *type *”的函数指示符将转换为类型为“pointer to function returning *type *”的表达式。
这意味着,如果
f
指定了一个函数,那么在一个调用中,比如f()
,f
会自动转换为&f
,结果是(&f)()
。这实际上是调用函数的“正确”方式,因为函数调用表达式需要指向函数的指针。现在考虑一下
*f
中发生了什么。f
会自动转换为&f
,所以我们有*&f
。在这个表达式中,*
运算符的结果是一个函数f
;它只是反转&
执行的操作。所以*&f
与f
相同。我们可以无限期地重复这一点:**f
自动转换为**&f
,即*f
,*f
自动转换为*&f
,*&f
自动转换为f
,f
自动转换为&f
。