为什么函数指针定义适用于任意数量的&符号&'还是星号'*'?

时间:2011-08-01 00:37:26

标签: c++ c function-pointers

为什么以下工作?

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)();
}

4 个答案:

答案 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 (*)()作为函数参数相同,并且可以通过*funcfunc或{{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