La réponse la plus simple, en supposant que vous ne soyez pas gêné par les aléas et les variations de format entre les différentes plateformes, est la norme suivante %p
la notation.
La norme C99 (ISO/IEC 9899:1999) indique au §7.19.6.1 ¶8 :
p
L'argument est un pointeur vers void
. La valeur du pointeur est convertie en une séquence de caractères d'impression, d'une manière définie par l'implémentation. d'implémentation.
(Dans la norme C11 - ISO/IEC 9899:2011 - l'information figure au §7.21.6.1 ¶8).
Sur certaines plates-formes, cela comprendra un 0x
et sur d'autres non, et les lettres peuvent être en minuscules ou en majuscules, et la norme C ne définit même pas que la sortie doit être hexadécimale bien que je ne connaisse aucune implémentation où elle ne le soit pas.
La question de savoir si l'on doit convertir explicitement les pointeurs avec une balise (void *)
cast. C'est être explicite, ce qui est généralement bon (c'est donc ce que je fais), et la norme dit "l'argument doit être un pointeur vers void
'. Sur la plupart des machines, on peut s'en sortir en omettant un cast explicite. Cependant, cela aurait de l'importance sur une machine où la représentation binaire d'une variable char *
pour un emplacement mémoire donné est différente de l'adresse ' tout autre pointeur ' pour le même emplacement mémoire. Il s'agirait d'une machine à adresse de mot, au lieu d'une machine à adresse d'octet. De telles machines ne sont pas courantes (probablement pas disponibles) de nos jours, mais la première machine sur laquelle j'ai travaillé après l'université en était une (ICL Perq).
Si vous n'êtes pas satisfait du comportement défini par l'implémentation de la fonction %p
puis utilisez C99 <inttypes.h>
y uintptr_t
à la place :
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
Cela vous permet d'ajuster la représentation à votre convenance. J'ai choisi d'avoir les chiffres hexadécimaux en majuscules pour que le nombre ait uniformément la même hauteur et que le creux caractéristique au début de 0xA1B2CDEF
apparaît ainsi, pas comme 0xa1b2cdef
qui monte et descend le long du numéro aussi. C'est votre choix, mais dans des limites très larges. Le site (uintptr_t)
cast est recommandé sans ambiguïté par GCC lorsqu'il peut lire la chaîne de format au moment de la compilation. Je pense qu'il est correct de demander le cast, bien que je sois sûr que certains ignoreront l'avertissement et s'en sortiront la plupart du temps.
demande Kerrek dans les commentaires :
Je suis un peu confus au sujet des promotions standard et des arguments variadiques. Est-ce que tous les pointeurs sont promus en void* ? Sinon, si int*
étaient, disons, de deux octets, et void*
était de 4 octets, alors ce serait clairement une erreur de lire 4 octets de l'argument, non ?
J'avais l'illusion que le standard C disait que tous les pointeurs d'objets devaient avoir la même taille, donc void *
y int *
ne peuvent pas être de tailles différentes. Cependant, ce que je pense être la section pertinente de la norme C99 n'est pas aussi catégorique (bien que je ne connaisse pas d'implémentation où ce que j'ai suggéré est vrai est en fait faux) :
§6.2.5 Types
¶26 Un pointeur vers void doit avoir les mêmes exigences de représentation et d'alignement qu'un pointeur vers un type de caractère. 39) De même, les pointeurs vers des versions qualifiées ou non qualifiées de types compatibles doivent avoir les mêmes exigences de représentation et d'alignement. Tous les pointeurs vers des types de structure doivent répondre aux mêmes exigences en matière de représentation et d'alignement. Tous les pointeurs vers des types d'union doivent répondre aux mêmes exigences de représentation et d'alignement. Les pointeurs vers d'autres types ne doivent pas nécessairement avoir les mêmes exigences en matière de représentation et d'alignement.
39) Les mêmes exigences de représentation et d'alignement sont censées impliquer l'interchangeabilité des arguments des fonctions, des valeurs de retour des fonctions et des membres des unions.
(C11 dit exactement la même chose dans la section §6.2.5, ¶28, et la note de bas de page 48).
Ainsi, tous les pointeurs vers des structures doivent avoir la même taille et partager les mêmes exigences d'alignement, même si les structures sur lesquelles pointent les pointeurs peuvent avoir des exigences d'alignement différentes. Il en va de même pour les unions. Les pointeurs de caractères et les pointeurs void doivent avoir la même taille et les mêmes exigences d'alignement. Les pointeurs sur les variations de int
(sens unsigned int
y signed int
) doivent avoir les mêmes exigences de taille et d'alignement les uns par rapport aux autres ; il en va de même pour les autres types. Mais le standard C ne dit pas formellement que sizeof(int *) == sizeof(void *)
. Oh bien, le SO est bon pour vous faire inspecter vos hypothèses.
La norme C n'exige définitivement pas que les pointeurs de fonction aient la même taille que les pointeurs d'objet. Cela était nécessaire pour ne pas casser les différents modèles de mémoire sur les systèmes de type DOS. Là, vous pouviez avoir des pointeurs de données de 16 bits mais des pointeurs de fonctions de 32 bits, ou vice versa. C'est pourquoi la norme C n'impose pas que les pointeurs de fonction puissent être convertis en pointeurs d'objet et vice versa.
Heureusement (pour les programmeurs qui ciblent POSIX), POSIX intervient et impose que les pointeurs de fonction et les pointeurs de données aient la même taille :
§2.12.3 Types de pointeurs
Tous les types de pointeurs de fonction doivent avoir la même représentation que le type pointeur vers void. La conversion d'un pointeur de fonction en void *
n'altère pas la représentation. A void *
résultant d'une telle conversion peut être reconvertie dans le type de pointeur de fonction original, en utilisant un cast explicite, sans perte d'information.
Remarque : La norme ISO C ne l'exige pas, mais elle est requise pour la conformité POSIX.
Donc, il semble que les castings explicites vers void *
sont fortement conseillés pour une fiabilité maximale du code lorsqu'on passe un pointeur à une fonction variadique telle que printf()
. Sur les systèmes POSIX, il est sûr de transformer un pointeur de fonction en un pointeur void pour l'impression. Sur d'autres systèmes, il n'est pas forcément sûr de le faire, ni de passer des pointeurs autres que void *
sans plâtre.