为什么 `p()` 和 `(*p)()` 都有效?

Why are both `p()` and `(*p)()` valid?

对于这个简单的代码来说:

int foo(void);
int (*p)(void);
p = foo;
p = &foo;
int a = p();
int b = (*p)();

上面的例子中,第3&4行都有效,第5&6行也都有效

事实上,如果你像玩函数指针那样尝试玩变量指针,你的程序肯定会死。那么为什么它对函数指针有效呢?

函数(更准确地说,函数指示符)被转换为指向函数的指针(除非它是 sizeof、[=11 的操作数=], 或一元 &).所以 foo&foo 是一回事,因为 foo 会自动转换为 &foo.

同理,p是函数的指针,所以*p是函数,所以自动转换为函数的地址。

事实上,因为 *p 立即转换回指针,所以您可以再次对它应用 *,如 int b = (**p)();。你可以一次又一次地这样做:int b = (************p)();.

来自 2011 C 标准(委员会草案 N1570),6.3.2.1 4:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, a function designator with type “function returning type” is converted to an expression that has type “pointer to function returning type”.

这只是 C 开发人员希望能够轻松使用 foo 等函数和 p 等函数指针这一事实的结果。如果您必须为一个写 foo() 而为另一个写 (*p)(),那会有点麻烦。或者至少打字更多。或者,如果您必须为一个写 (&foo)() 而为另一个写 p(),这又是一次更多的打字。所以他们只是说只要你写一个函数,我们就会自动把它变成指针,你不用再输入就可以调用它。

我想他们本可以说函数调用运算符可以接受函数或指向函数的指针,但他们选择了自动转换。

推论:如果你不知道某个东西是函数,还是指向函数的指针,还是指向指向函数的指针的指针,等等,就拍拍前面一百个星号,编译到函数就停止

根据 C11 标准的 §6.5.2.2/1,在函数调用中:

The expression that denotes the called function shall have type pointer to function returning void or returning a complete object type other than an array type.

现在,在大多数表达式中,函数指示符被转换为函数指针 (§6.3.2.1/4):

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, a function designator with type ''function returning type'' is converted to an expression that has type ''pointer to function returning type''.

因此,在int a = p();这样的函数调用中,函数指示符p被转换为函数指针,在后缀运算符()之前的表达式中需要它函数调用。

但是,根据 §6.5.3.2/4 关于间接运算符的说法:

If the operand points to a function, the result is a function designator

所以,在int b = (*p)();这样的函数调用中,函数指示符p被转换为函数指针,间接运算符作用于这个函数指针的结果就是一个函数指示符, 它本身被转换为函数调用的后缀表达式中所需的函数指针。

通过上面的§6.5.2.2/1,foo被转换为表达式p = foo中的函数指针。但是,在表达式 p = &foo 中,函数指示符 而不是 转换为函数指针。这里,§6.5.3.2/3 表示:

The unary & operator yields the address of its operand. If the operand has type ''type'', the result has type ''pointer to type''.... Otherwise, the result is a pointer to the object or function designated by its operand.

因此表达式 &foo 的计算结果为指向由 foo 指定的函数的指针,正如预期的那样。