web-dev-qa-db-fra.com

Pourquoi les définitions de pointeur de fonction fonctionnent-elles avec un certain nombre d'esperluettes '&' ou d'astérisques '*'?

Pourquoi les travaux suivants fonctionnent-ils?

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

Il y a quelques pièces à cela 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; fonctionne: foo est implicitement converti en un pointeur sur lui-même et ce pointeur est assigné à p1_foo.

L'unaire &, Lorsqu'il est appliqué à une fonction, renvoie un pointeur vers la fonction, tout comme il renvoie l'adresse d'un objet lorsqu'il est appliqué à un objet. Pour les pointeurs vers des fonctions ordinaires, il est toujours redondant en raison de la conversion implicite de pointeur de fonction en fonction. Dans tous les cas, c'est pourquoi void (*p3_foo)() = &foo; fonctionne.

L'unaire *, Lorsqu'il est appliqué à un pointeur de fonction, produit la fonction pointée, tout comme il renvoie l'objet pointé lorsqu'il est appliqué à un pointeur ordinaire sur un objet.

Ces règles peuvent être combinées. Considérez votre avant-dernier exemple, **foo:

  • Tout d'abord, foo est implicitement converti en un pointeur sur lui-même et le premier * Est appliqué à ce pointeur de fonction, produisant à 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 implicitement converti à nouveau en un pointeur de fonction et affecté à la variable.

Vous pouvez ajouter autant de * Que vous le souhaitez, le résultat est toujours le même. Plus * S, plus on est de fous.

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

  • Premièrement, foo est implicitement converti en un pointeur sur lui-même; l'unaire * est appliqué, ce qui donne à nouveau foo.
  • Ensuite, & Est appliqué à foo, ce qui donne un pointeur sur foo, qui est affecté à la variable.

Le & Ne peut cependant être appliqué qu'à une fonction, pas à une fonction qui a été convertie en pointeur de fonction (à moins, bien sûr, que le pointeur de fonction soit une variable, auquel cas le résultat est un pointeur- vers un pointeur vers une fonction; par exemple, vous pouvez ajouter à 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 r. Cependant, &*&*&*&*&*&*foo Fonctionnerait, tout comme &******&foo, Car dans ces 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; (*p1_foo)(); et (p1_foo)(); ont tous deux le même résultat, encore une fois en raison de la conversion de pointeur de fonction en fonction.

208
James McNellis

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

Du point de 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 comme une adresse, ce qui conduit probablement à la conception qu'une fonction est "la même" que l'adresse vers laquelle elle pointe.

6
madumlao