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

时间:2017-11-16 01:47:58

标签: c language-lawyer function-pointers history

说这个简单的代码:

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

在上面的示例中,第3行和第4行都有效,第5行和第6行也都有效。

事实上,如果你在使用函数指针时尝试使用类似的变量指针,那么你的程序肯定会死掉。那为什么它对函数指针有效呢?

2 个答案:

答案 0 :(得分:7)

一个函数(更准确地说,一个函数指示符)被转换为一个指向函数的指针(除非它是sizeof_Alignof的操作数,或者一元&)。因此foo&foo是相同的,因为foo会自动转换为&foo

类似地,p是指向函数的指针,因此*p是函数,因此它会自动转换为函数的地址。

事实上,由于*p会立即转换回指针,因此您可以再次将*应用于int b = (**p)();,就像int b = (************p)();一样。你可以一次又一次地做到这一点:sizeof

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

  

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

这只是因为C开发人员希望能够轻松使用p等函数和foo()之类的函数指针。如果你必须为另一个写(*p)()而为另一个写(&foo)(),那将会有点麻烦。或至少更多打字。或者,如果你必须为另一个写p()而为另一个写invisible,那就再打字了。所以他们只是在你编写函数时说,我们会自动将它作为指针,你可以不用再打字就调用它。

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

推论:如果你不知道某个东西是函数,还是指向函数的指针,或指向函数指针的指针,等。,只需要在前面拍一百个星号当它到达函数时,编译器将停止。

答案 1 :(得分:2)

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

  

表示被调用函数的表达式应具有指向函数的类型指针,该函数返回void或返回除数组类型之外的完整对象类型。

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

  

函数指示符是具有函数类型的表达式。除非它是 sizeof 运算符, _Alignof 运算符或一元& 运算符的操作数,否则为类型为''的函数指示符函数返回类型''被转换为一个表达式,其类型为''指向函数返回类型的指针''。

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

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

  

如果操作数指向函数,则结果是函数指示符

因此,在诸如int b = (*p)();之类的函数调用中,函数指示符p被转换为函数指针,并且作用于此函数指针的间接运算符的结果是函数指示符,本身被转换为函数调用的后缀表达式中所需的函数指针。

根据上面的§6.5.2.2/ 1,foo被转换为表达式p = foo中的函数指针。但是,在表达式p = &foo中,函数指示符转换为函数指针。在此,§6.5.3.2/3声明:

  

一元& 运算符会生成其操作数的地址。如果操作数具有类型''type'',则结果具有类型''指向类型''的指针....否则,结果是指向由其操作数指定的对象或函数的指针。

因此,表达式&foo按预期计算指向foo指定的函数的指针。