C语言 这两种函数指针调用语法变体是如何工作的?

vbkedwbf  于 2023-05-16  发布在  其他
关注(0)|答案(4)|浏览(103)

我是一个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版本的语法糖吗?

kiayqfof

kiayqfof1#

这是一块语法/语义糖,AFAIK,从最早的C语言版本起就开始工作了。如果你把函数看作指向代码的指针,这是有意义的。
使函数指针以这种方式工作所需的唯一特殊规则是,间接指向函数指针会返回相同的指针(因为无论如何都不能操作标准C中的代码):当f是函数指针时,则f == (*f) == (**f),等等。
(旁白:要注意像void (*f)()这样的声明。空参数列表表示仅在返回类型上匹配的旧式C89之前的函数声明。对于类型安全,首选void (*f)(void)。)

db2dz4w8

db2dz4w82#

函数调用表达式的形式总是“函数指针”、“圆括号”、“参数”、“圆括号”。为了让你不必每次都拼出(&printf)("Hello World\n"),有一个单独的规则,表示函数的表达式衰减到相应的函数指针。
由于函数指针可以被解引用以给予再次表示函数的表达式,因此这将再次衰减,因此您可以继续解引用,并且会有很多衰减:

(&f)();   // no decay (decay does not happen when the expression
          //           is the operand of &)
f();      // one decay
(*f)();   // two decays
(**f)();  // three decays

1)早期的Perl有这样的函数语法。

0qx6xfy6

0qx6xfy63#

在C中,你可以像这样调用函数:

f();
(*f)();
(**f)();
(********f)();
(*****************************f)();

都是有效的。在C中,解引用或获取函数的地址只是计算为指向该函数的指针,而解引用函数指针只是计算回函数指针。C的设计方式是函数名标识符和变量持有函数指针的含义相同:CODE存储器地址。它允许通过在标识符或变量上使用call ()语法跳转到该内存。
最后,但也是最不重要的,标准说:

C11:6.3.2.1:

4函数指示符是具有函数类型的表达式。除非它是sizeof运算符、_Alignof运算符、65)或一元&运算符的操作数,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回类型的指针”的表达式。

mccptt67

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;它只是反转&执行的操作。所以*&ff相同。我们可以无限期地重复这一点:**f自动转换为**&f,即*f*f自动转换为*&f*&f自动转换为ff自动转换为&f

相关问题