Pourquoi les définitions de pointeur de fonction fonctionnent-elles avec un nombre quelconque de perluètes ‘&’ ou d’astérisques ‘*’?

Pourquoi le travail suivant?

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

Il y a quelques éléments qui permettent à toutes ces combinaisons d’opérateurs de fonctionner de la même manière.

La raison fondamentale pour laquelle tout cela fonctionne est qu’une fonction (comme foo ) est implicitement convertible en un pointeur vers la fonction. C’est pourquoi void (*p1_foo)() = foo; works: foo est implicitement converti en un pointeur vers lui-même et ce pointeur est assigné à p1_foo .

Unary & , appliqué à une fonction, renvoie un pointeur vers la fonction, tout comme il renvoie l’adresse d’un object lorsqu’il est appliqué à un object. Pour les pointeurs vers des fonctions ordinaires, il est toujours redondant en raison de la conversion implicite de fonction à fonction. En tout cas, c’est pourquoi void (*p3_foo)() = &foo; travaux.

L’unaire * , appliqué à un pointeur de fonction, fournit la fonction pointée, tout comme il renvoie l’object pointé lorsqu’il est appliqué à un object sur un pointeur ordinaire.

Ces règles peuvent être combinées. Considérez votre deuxième exemple, **foo :

  • Tout d’abord, foo est implicitement converti en un pointeur vers lui-même et le premier * est appliqué à ce pointeur de fonction, ce qui donne à nouveau la fonction foo .
  • Ensuite, le résultat est à nouveau implicitement converti en un pointeur sur lui-même et le second * est appliqué, produisant à nouveau la fonction foo .
  • Il est ensuite converti implicitement en un pointeur de fonction et affecté à la variable.

Vous pouvez append autant de * que vous voulez, le résultat est toujours le même. Le plus * s, le plus joyeux.

Nous pouvons également considérer votre cinquième exemple, &*foo :

  • Tout d’abord, foo est implicitement converti en un pointeur sur lui-même; l’unaire * est appliqué, cédant de nouveau au foo .
  • Ensuite, le & est appliqué à foo , donnant un pointeur à foo , qui est assigné à la variable.

Le & peut seulement être appliqué à une fonction, mais pas à une fonction qui a été convertie en un pointeur de fonction (à moins, bien sûr, que le pointeur de fonction soit une variable, auquel cas le résultat est un pointeur sur un pointeur -to-a-function, par exemple, vous pouvez append à votre liste void (**pp_foo)() = &p7_foo; ).

C’est pourquoi &&foo ne fonctionne pas: &foo n’est pas une fonction; c’est un pointeur de fonction qui est une valeur. Cependant, &*&*&*&*&*&*foo fonctionnerait, comme le ferait &******&foo , car dans les deux expressions, le & est toujours appliqué à une fonction et non à un pointeur de fonction rvalue .

Notez également que vous n’avez pas besoin d’utiliser l’unaire * pour effectuer l’appel via le pointeur de fonction; les deux (*p1_foo)(); et (p1_foo)(); avoir le même résultat, encore une fois en raison de la conversion fonction-fonction-pointeur.

Je pense qu’il est également utile de se rappeler que C est juste une abstraction pour la machine sous-jacente et c’est l’un des endroits où cette abstraction fuit.

Du sharepoint vue de l’ordinateur, une fonction n’est qu’une adresse mémoire qui, si elle est exécutée, exécute d’autres instructions. Ainsi, une fonction en C est elle-même modélisée en tant qu’adresse, ce qui conduit probablement à la conception selon laquelle une fonction est “identique” à l’adresse indiquée.