说这个简单的代码:
int foo(void);
int (*p)(void);
p = foo;
p = &foo;
int a = p();
int b = (*p)();
在上面的示例中,第3行和第4行都有效,第5行和第6行也都有效。
事实上,如果你在使用函数指针时尝试使用类似的变量指针,那么你的程序肯定会死掉。那为什么它对函数指针有效呢?
答案 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
指定的函数的指针。