Adresse d’impression de la fonction à l’aide d’un pointeur

J’essaie d’imprimer deux fois une adresse de pointeur de fonction à l’aide de l’instruction printf indiquée dans le code ci-dessous …

 class B { public: int fun() { } }; int main() { int (B::*pb)()=&B::fun; printf("ptr:%x | %x\n",pb,pb); //Output is ptr:8048730 | 0 } 

Quand je transmets la même variable à printf il devrait afficher la même valeur, mais après avoir obtenu le résultat, je suis surpris.

Quelqu’un peut-il expliquer la raison de ceci ou quelque part que je fais mal.

gcc version 4.8.2 (GCC)

Si vous activez les avertissements (passez l’indicateur -Wall au compilateur), le compilateur vous dira ce que vous faites mal:

 warning: format '%x' expects argument of type 'unsigned int', but argument 2 has type 'int (B::*)()' [-Wformat] warning: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'int (B::*)()' [-Wformat] 

En général, je ne peux que vous encourager à activer les avertissements.

Maintenant, un hack (comportement indéfini) est le suivant:

std::printf("ptr:%p | %p\n",(void*)funcptr,(void*)funcptr);

Cela donne toujours l’avertissement suivant:

warning: converting from 'int (B::*)()' to 'void*' [-Wpmf-conversions]

mais le programme imprime la même adresse deux fois comme vous le souhaitez. C’est encore un comportement indéfini, voir Comment formater un pointeur de fonction? En suivant la réponse acceptée (code volé sans vergogne à partir de là), on pourrait imprimer l’adresse comme suit:

 #include  class B { public: int fun() { return 0; } }; int main() { int (B::*funcptr)()=&B::fun; unsigned char *p = (unsigned char *)&funcptr; for (unsigned int i = 0; i < sizeof funcptr; ++i) { std::printf("%02x ", p[i]); } std::putchar('\n'); return 0; } 

Selon cette réponse, c'est le seul moyen légal d'y parvenir. Cela ne donne aucun avertissement.

La fonction pointeur-à-membre contient “l’adresse relative” de l’endroit où la fonction se trouve dans la présentation de la classe. Apportez les modifications suivantes au code:

 class B { public: static int fun() // Add static keyword { } }; int main() { int (*pb)()=&B::fun; // Change B::*pb for *pb // You should use %p instead of %x as suggested by @Praetorian printf("ptr:%p | %p\n",pb,pb); //Now ouput is the same. } 

Un membre statique n’a pas de “partie de la classe”. Vous pouvez faire un autre test, essayez simplement d’imprimer l’adresse d’une fonction non membre.

Vous trouverez plus d’informations sur le pointeur sur les fonctions membres ici .

La raison en est que:

  1. pb (comme écrit) est un pointeur sur une fonction membre, pas un pointeur ordinaire sur une fonction.
  2. La taille d’un pointeur sur la fonction membre est inconnue; il peut être ou non identique à la taille d’autres pointeurs ou d’entiers non signés. Cela pourrait facilement être plus que l’habituel 32 bits (4 octets), car il a plus d’informations à encoder.
  3. Le spécificateur de format% x concerne uniquement les entiers non signés. En supposant que l’architecture “habituelle”, cela signifie 32 bits ou 4 octets.
  4. La séquence d’appel vargs utilisée par printf extrait simplement deux entrées non signées de la liste des arguments et les affiche. Si les arguments qui lui sont transmis ne correspondent pas, les arguments imprimés ne s’alignent pas et vous obtenez ce que vous obtenez.

Je pourrais spéculer sur la nature exacte des différents nombres, mais il est difficile d’en être sûr sans connaître le compilateur. Je l’ai essayé sur VS2010 et j’ai obtenu un entier parfaitement sensible de 6 octets imprimé deux fois. Allez comprendre.

Vous pouvez vous le prouver en prenant la taille de ce pointeur (qui devrait être supérieure à 4) ou en la changeant en un pointeur sur une fonction, qui a presque toujours la même taille que les autres pointeurs.

Veuillez noter que la norme ne garantit rien sur la taille des pointeurs vers des fonctions par rapport à d’autres pointeurs. La signification précise ou le comportement de% p et son fonctionnement avec les pointeurs de fonction ne sont pas non plus garantis.

Comme nous le soaps tous, une fonction ptr équivaut à un nombre non signé 32 bits sur une machine 32 bits. Si vous voulez voir la valeur d’un ptr func, vous pouvez utiliser reinterpret_cast pour la convertir en un

 unsigned int a = reinterpret_cast(fptr); cout << hex << a << endl;