为什么以下工作?
void foo() {
cout << "Foo to you too!\n";
};
int main() {
void (*p1_foo)() = foo;
void (*p2_foo)() = *foo;
void (*p3_foo)() = &foo;
void (*p4_foo)() = *&foo;
void (*p5_foo)() = &*foo;
void (*p6_foo)() = **foo;
void (*p7_foo)() = **********************foo;
(*p1_foo)();
(*p2_foo)();
(*p3_foo)();
(*p4_foo)();
(*p5_foo)();
(*p6_foo)();
(*p7_foo)();
}
答案 0 :(得分:207)
有一些部分允许所有这些运算符组合以相同的方式工作。
所有这些工作的根本原因是函数(如foo
)可以隐式转换为指向函数的指针。这就是void (*p1_foo)() = foo;
有效的原因:foo
被隐式转换为指向自身的指针,指针被分配给p1_foo
。
一元&
,当应用于函数时,会产生一个指向函数的指针,就像它在应用于对象时产生对象的地址一样。对于指向普通函数的指针,由于隐式的函数 - 函数 - 指针转换,它总是冗余的。无论如何,这就是void (*p3_foo)() = &foo;
有效的原因。
一元*
,当应用于函数指针时,会产生指向函数,就像它在应用于指向对象的普通指针时产生指向对象一样。
可以组合这些规则。考虑你的倒数第二个例子,**foo
:
foo
被隐式转换为指向自身的指针,第一个*
被应用于该函数指针,再次产生函数foo
。*
,再次产生函数foo
。您可以根据需要添加任意数量的*
,结果始终相同。 *
越多越好。
我们还可以考虑您的第五个例子,&*foo
:
foo
被隐式转换为指向自身的指针;应用了一元*
,再次产生foo
。&
应用于foo
,产生指向foo
的指针,该指针已分配给变量。 &
只能应用于函数,而不能应用于已转换为函数指针的函数(当然,除非函数指针是变量,在这种情况下结果是指向指向函数的指针;例如,您可以添加到列表void (**pp_foo)() = &p7_foo;
)。
这就是&&foo
不起作用的原因:&foo
不是函数;它是一个函数指针,它是一个右值。但是,&*&*&*&*&*&*foo
和&******&foo
一样可以正常工作,因为在这两个表达式中,&
始终应用于函数而不是rvalue函数指针。
另请注意,您无需使用一元*
通过函数指针进行调用; (*p1_foo)();
和(p1_foo)();
都有相同的结果,这也是因为函数到函数指针的转换。
答案 1 :(得分:5)
我认为记住C只是底层机器的抽象也是有帮助的,这是抽象泄漏的地方之一。
从计算机的角度来看,函数只是一个内存地址,如果执行,则执行其他指令。因此,C中的函数本身被建模为一个地址,这可能导致函数的设计是相同的&#34;作为它指向的地址。
答案 2 :(得分:0)
&
和*
是在C中声明为函数的符号上的幂等运算,表示func == *func == &func == *&func
,因此*func == **func
这意味着类型int ()
与int (*)()
作为函数参数相同,并且可以通过*func
,func
或{{1} }。 &func
与(&func)()
相同。 Godbolt link.
函数实际上是一个地址,因此func()
和*
没有意义,并且编译器选择不将其解释为func的地址,而不会产生错误。
&
在声明为函数指针的符号上将获得指针的地址(因为它现在具有单独的用途),而&
和funcp
将相同< / p>
答案 3 :(得分:-1)
如果您对@JamesMcNellis 的回答仍然不太相信,这里有一个证明。这是来自 Clang 编译器的 AST(抽象语法树)。抽象语法树是编译器内部程序结构的内部表示。
void func1() {};
void test() {
func1();
(*func1)();
(&func1)();
void(*func1ptr)(void) = func1;
func1ptr();
(*func1ptr)();
//(&func1ptr)();//error since func1ptr is a variable, &func1ptr is its address which is not callable.
}
AST:
//func1();
|-CallExpr //call the pointer
| `-ImplicitCastExpr //implicitly convert func1 to pointer
| `-DeclRefExpr //reference func1
//(*func1)();
|-CallExpr //call the pointer
| `-ImplicitCastExpr //implicitly convert the funtion to pointer
| `-ParenExpr //parentheses
| `-UnaryOperator //* operator get function from the pointer
| `-ImplicitCastExpr //implicitly convert func1 to pointer
| `-DeclRefExpr //reference func1
//(&func1)();
|-CallExpr //call the pointer
| `-ParenExpr //parentheses
| `-UnaryOperator //& get pointer from func1
| `-DeclRefExpr //reference func1
//void(*func1ptr)(void) = func1;
|-DeclStmt //define variable func1ptr
| `-VarDecl //define variable func1ptr
| `-ImplicitCastExpr //implicitly convert func1 to pointer
| `-DeclRefExpr //reference func1
//func1ptr();
|-CallExpr //call the pointer
| `-ImplicitCastExpr //implicitly convert func1ptr to pointer
| `-DeclRefExpr //reference the variable func1ptr
//(*func1ptr)();
`-CallExpr //call the pointer
`-ImplicitCastExpr //implicitly convert the function to pointer
`-ParenExpr //parentheses
`-UnaryOperator //* get the function from the pointer
`-ImplicitCastExpr //implicitly convert func1ptr to pointer
`-DeclRefExpr //reference the variable func1ptr