83 votes

Comment se déroule le déréférencement d'un pointeur de fonction?

Pourquoi et comment un déréférencement d'un pointeur de fonction juste "ne rien faire"?

C'est ce que je suis en train de parler:

#include<stdio.h>

void hello() { printf("hello"); }

int main(void) { 
    (*****hello)(); 
}

À partir d'un commentaire sur ici:

déréférencement de pointeurs de fonction juste fine, mais le résultat de la fonction désignation sera immédiatement de nouveau converti en un pointeur de fonction


Et d'une réponse ici:

Un déréférencement (façon de penser) la fonction de pointeur signifie: l'accès à un CODE de la mémoire, comme il le serait de DONNÉES de la mémoire.

Pointeur de fonction n'est pas supposé être déréférencé. Au lieu de cela, il est appelé.

Je voudrais utiliser un nom de "déréférencer" côté avec "l'appel". C'est OK.

De toute façon: C est conçu de telle manière qui à la fois la fonction nom de l'identificateur bien que variable contenant la fonction pointeur de dire la même: adresse CODE de la mémoire. Et il permet de sauter à la la mémoire en utilisant l'appel () syntaxe soit sur un identificateur ou une variable.


Comment exactement ne déréférencement d'un pointeur de fonction de travail?

69voto

Norman Ramsey Points 115730

Ce n'est pas tout à fait la bonne question. Pour C, au moins, la bonne question est

Ce qui arrive à une valeur de la fonction dans une rvalue contexte?

(Une rvalue contexte est partout un nom ou une référence apparaît à l'endroit où elle doit être utilisée comme une valeur, plutôt qu'en un lieu, fondamentalement, n'importe où sauf sur la partie gauche d'une affectation. Le nom lui-même vient de la droite, du côté de l'affectation).

OK, donc ce qui arrive à une valeur de la fonction dans une rvalue contexte? Il est immédiatement et implicitement converti en un pointeur vers la fonction d'origine de la valeur. Si vous déréférencement de pointeur avec *, vous obtenez la même valeur de la fonction de retour à nouveau, ce qui est immédiatement et implicitement converti en un pointeur. Et vous pouvez le faire autant de fois que vous le souhaitez.

Deux des expériences similaires, vous pouvez essayer:

  • Qu'advient-il si vous déréférencement d'un pointeur de fonction dans une lvalue contexte—la partie gauche d'une affectation. (La réponse sera ce que vous attendez, si vous gardez à l'esprit que les fonctions sont immuables.)

  • Un tableau de la valeur est également converti en un pointeur dans une lvalue contexte, mais il est converti en un pointeur vers l' élément de type, pas un pointeur vers le tableau. Un déréférencement il va donc vous donner un élément, pas un tableau, et la folie de vous montrer n'a pas lieu.

Espérons que cette aide.

P. S. pour ce qui est pourquoi une valeur de la fonction est implicitement converti en un pointeur, la réponse est que, pour ceux d'entre nous qui utilisent des pointeurs de fonction, c'est d'une grande commodité de ne pas avoir à utiliser &'s partout. Il y a une double pratique: un pointeur de fonction en appel la position est automatiquement convertie en une valeur de la fonction, de sorte que vous n'avez pas à écrire * d'appel à l'aide d'un pointeur de fonction.

P. P. S. à la différence de fonctions en C, C++ les fonctions peuvent être surchargés, et je ne suis pas qualifié pour soumettre des commentaires sur la façon dont la sémantique fonctionne en C++.

10voto

Potatoswatter Points 70305

C++03 §4.3/1:

Une lvalue de fonction de type T peut être convertie en une valeur r de type "pointeur vers T." Le résultat est un pointeur vers la fonction.

Si vous tentez une opération non valide sur une fonction de référence, tels que le unaire * de l'opérateur, la première chose que le langage tente est une conversion standard. C'est comme la conversion d'un int lors de l'ajout à un float. À l'aide de * sur une fonction de référence provoque la langue pour prendre son pointeur au lieu de cela, dans votre exemple, est la place 1.

Un autre cas où cela s'applique lors de l'affectation d'un pointeur de fonction.

void f() {
    void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr.
    recurse(); // call operator is defined for pointers
}

Notez que ce n'est pas dans l'autre sens.

void f() {
    void (&recurse)() = &f; // "&f" is a pointer; ERROR can't convert to ref.
    recurse(); // OK - call operator is *separately* defined for references
}

Fonction les variables de référence sont bien car ils (en théorie, je n'ai jamais testé) indication pour le compilateur qui indirects de la branche peut être inutile, si initialisées dans un cadre englobant.

En C99, déréférencement d'un pointeur de fonction génère une fonction de désignation. §6.3.2.1/4:

Une fonction de désignation est une expression qui a la fonction de type. Sauf quand c'est l'opérande de l'opérateur sizeof ou unaire & de l'opérateur, une fonction de désignation de type ‘fonction de retour de type" est converti en une expression qui a le type ‘pointeur de fonction de retour de type".

C'est plus comme Norman réponse, mais notamment C99 n'a pas de notion de rvalues.

2voto

MSalters Points 74024

Comment fonctionne exactement un déréférencement d'un pointeur de fonction de travail?

Deux étapes. La première étape est au moment de la compilation, la seconde au moment de l'exécution.

Dans la première étape, le compilateur voit qu'il a un pointeur et un contexte dans lequel le pointeur est déréférencé (comme (*pFoo)() ) de sorte qu'il génère du code pour cette situation, le code qui sera utilisé dans l'étape 2.

Dans l'étape 2, au moment de l'exécution le code est exécuté. Le pointeur contient des octets indiquant la fonction qui doit être exécutée suivant. Ces octets sont en quelque sorte chargé dans la CPU. Un cas courant est un CPU avec un explicite CALL [register] enseignement. Sur de tels systèmes, un pointeur de fonction peut être simplement l'adresse d'une fonction dans la mémoire, et la derefencing code ne fait rien de plus que de se charger de cette adresse dans un registre suivi par un CALL [register] enseignement.

1voto

Hans Passant Points 475940

Mettez vous à la place du compilateur écrivain. Un pointeur de fonction est bien définie sens, il est un pointeur vers une goutte d'octets qui représentent le code machine.

Que faites-vous lorsque le programmeur déréférence un pointeur de fonction? Ne vous prenez la première (ou 8) octets du code machine et de les réinterpréter que comme un pointeur? Les chances sont d'environ 2 milliards de dollars pour l'une que cela ne fonctionnera pas. Avez-vous déclarer UB? Beaucoup de qui va autour déjà. Ou avez-vous simplement ignorer la tentative? Vous connaissez la réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X