La réponse la plus simple, en supposant que vous ne vous souciez pas des aléas et des variations de format entre les différentes plates-formes, est la %p
notation standard .
La norme C99 (ISO / CEI 9899: 1999) dit au §7.19.6.1 ¶8:
p
L'argument doit être 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.
(Dans C11 - ISO / CEI 9899: 2011 - les informations se trouvent au §7.21.6.1 ¶8.)
Sur certaines plates-formes, cela inclura un début 0x
et sur d'autres non, et les lettres pourraient être en minuscules ou en majuscules, et le standard C ne définit même pas qu'il s'agira d'une sortie hexadécimale bien que je sache aucune implémentation là où ce n'est pas le cas.
Il est quelque peu ouvert à débattre de la nécessité de convertir explicitement les pointeurs avec une (void *)
distribution. C'est être explicite, ce qui est généralement bien (c'est donc ce que je fais), et la norme dit que «l'argument sera un pointeur vers void
». Sur la plupart des machines, vous vous en sortiriez en omettant une distribution explicite. Cependant, cela serait important sur une machine où la représentation binaire d'une char *
adresse pour un emplacement mémoire donné est différente de l' adresse « pointeur autre chose » pour le même emplacement mémoire. Ce serait une machine à adressage par mot, au lieu d'adressage par octet. De telles machines ne sont pas courantes (probablement pas disponibles) ces jours-ci, 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 %p
, utilisez C99 <inttypes.h>
et à la uintptr_t
place:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
Cela vous permet d'affiner la représentation en fonction de vous-même. J'ai choisi d'avoir les chiffres hexadécimaux en majuscules pour que le nombre soit uniformément de la même hauteur et que le creux caractéristique au début de 0xA1B2CDEF
apparaisse ainsi, pas comme celui 0xa1b2cdef
qui descend le long du nombre aussi. Votre choix cependant, dans des limites très larges. Le (uintptr_t)
cast est sans ambiguïté recommandé 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 casting, même si je suis sûr que certains ignoreraient l'avertissement et s'en tireraient la plupart du temps.
Kerrek demande 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 comme annulés *? Sinon, s'il y int*
avait, disons, deux octets et void*
4 octets, alors ce serait clairement une erreur de lire quatre octets de l'argument, non?
J'étais dans l'illusion que la norme C dit que tous les pointeurs d'objet doivent être de la même taille, donc void *
et 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 si catégorique (bien que je ne connaisse pas une 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 de types compatibles doivent avoir les mêmes exigences de représentation et d'alignement. Tous les pointeurs vers des types de structure doivent avoir les mêmes exigences de représentation et d'alignement les uns que les autres. Tous les pointeurs vers des types d'union doivent avoir les mêmes exigences de représentation et d'alignement les uns que les autres. Les pointeurs vers d'autres types n'ont pas besoin d'avoir les mêmes exigences de représentation ou d'alignement.
39) Les mêmes exigences de représentation et d'alignement sont censées impliquer l'interchangeabilité en tant qu'arguments de fonctions, valeurs de retour de fonctions et membres d'unions.
(C11 dit exactement la même chose dans la section §6.2.5, ¶28 et note de bas de page 48.)
Ainsi, tous les pointeurs vers des structures doivent être de la même taille les uns que les autres et doivent partager les mêmes exigences d'alignement, même si les structures vers lesquelles pointent les pointeurs peuvent avoir des exigences d'alignement différentes. De même pour les syndicats. Les pointeurs de caractères et les pointeurs vides doivent avoir les mêmes exigences de taille et d'alignement. Les pointeurs vers des variations de int
(signification unsigned int
et signed int
) doivent avoir les mêmes exigences de taille et d'alignement les uns que les autres; de même pour les autres types. Mais la norme C ne le dit pas formellement sizeof(int *) == sizeof(void *)
. Eh bien, SO est bon pour vous faire inspecter vos hypothèses.
Le standard 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 pourriez avoir des pointeurs de données 16 bits mais des pointeurs de fonction 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 ciblant POSIX), POSIX intervient dans la brèche et exige que les pointeurs de fonction et les pointeurs de données soient de la même taille:
§2.12.3 Types de pointeurs
Tous les types de pointeur de fonction doivent avoir la même représentation que le pointeur de type vers void. La conversion d'un pointeur de fonction en void *
ne doit pas modifier la représentation. Une void *
valeur résultant d'une telle conversion peut être reconvertie dans le type de pointeur de fonction d'origine, à l'aide d'un cast explicite, sans perte d'informations.
Remarque: la norme ISO C ne l'exige pas, mais elle est requise pour la conformité POSIX.
Ainsi, il semble que les transtypages explicites en void *
sont fortement recommandés pour une fiabilité maximale dans le code lors du passage d'un pointeur vers une fonction variadique telle que printf()
. Sur les systèmes POSIX, il est sûr de convertir un pointeur de fonction en pointeur vide pour l'impression. Sur d'autres systèmes, ce n'est pas nécessairement sûr de faire cela, ni nécessairement de passer des pointeurs autrement que void *
sans lancer.
void*
? Sinon, s'il yint*
avait, disons, deux octets etvoid*
4 octets, alors ce serait clairement une erreur de lire quatre octets de l'argument, non?dlsym()
fonction à la place. Un jour, j'écrirai le changement ... mais «un jour» n'est pas «aujourd'hui».void *
? Hmm, je vois votre commentaire ici . Puisque seule une conversion d'un wat est nécessaire (pointeur de fonction versvoid *
), cela fonctionne alors?void *
et retour sans perte d'informations. Pragmatiquement, il existe très peu de machines où la taille d'un pointeur de fonction n'est pas la même que la taille d'un pointeur d'objet. Je ne pense pas que la norme fournisse une méthode d'impression d'un pointeur de fonction sur des machines où la conversion est problématique.p
est le spécificateur de conversion pour imprimer des pointeurs. Utilisez ceci.N'oubliez pas que l'omission de la
p
conversion est un comportement indéfini et que l'impression avec le spécificateur de conversion est effectuée d'une manière définie par l'implémentation.la source
Utilisez
%p
, pour "pointer", et n'utilisez rien d'autre *. Vous n'êtes pas garanti par la norme que vous êtes autorisé à traiter un pointeur comme n'importe quel type d'entier particulier, donc vous obtiendriez en fait un comportement indéfini avec les formats intégraux. (Par exemple,%u
attend ununsigned int
, mais que se passe-t-il sivoid*
a une taille ou un alignement différent de celuiunsigned int
?)*) [Voir la bonne réponse de Jonathan!] Alternativement
%p
, vous pouvez utiliser des macros spécifiques au pointeur de<inttypes.h>
, ajoutées dans C99.Tous les pointeurs d'objet sont implicitement convertibles
void*
en C, mais pour passer le pointeur en tant qu'argument variadique, vous devez le convertir explicitement (car les pointeurs d'objet arbitraires ne sont que convertibles , mais pas identiques aux pointeurs void):la source
void *
(bien queprintf()
vous ayez techniquement besoin de la conversion explicite, car c'est une fonction variadique). Les pointeurs de fonction ne sont pas nécessairement convertibles envoid *
.void *
et retournés en pointeur de fonction sans perte; heureusement, cependant, POSIX l'exige explicitement (en notant qu'il ne fait pas partie du standard C). Donc, en pratique, vous pouvez vous en tirer (conversionvoid (*function)(void)
versvoid *
et retour versvoid (*function)(void)
), mais strictement cela n'est pas exigé par le standard C.%u
!%u
et%lu
sont faux sur toutes les machines , pas certaines machines. La spécification deprintf
est très claire que lorsque le type passé ne correspond pas au type requis par le spécificateur de format, le comportement n'est pas défini. Que la taille des types corresponde (qui peut être vraie ou fausse, selon la machine) n'est pas pertinente; ce sont les types qui doivent correspondre, et ils ne le seront jamais.Au lieu des autres (très bonnes) réponses, vous pouvez effectuer un cast vers
uintptr_t
ouintptr_t
(à partir destdint.h
/inttypes.h
) et utiliser les spécificateurs de conversion d'entiers correspondants. Cela permettrait une plus grande flexibilité dans la façon dont le pointeur est formaté, mais à proprement parler, une implémentation n'est pas nécessaire pour fournir ces typedefs.la source
#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
est-il un comportement indéfini pour imprimer l'adresse de la variable à l'aide du%u
spécificateur de format? L'adresse de la variable dans la plupart des cas est positive, puis-je utiliser à la%u
place de%p
?%u
est un format deunsigned int
type et ne peut pas être utilisé avec un argument pointeur versprintf
.Vous pouvez utiliser
%x
ou%X
ou%p
; tous sont corrects.%x
, l'adresse est donnée en minuscules, par exemple:a3bfbc4
%X
, l'adresse est indiquée en majuscules, par exemple:A3BFBC4
Les deux sont corrects.
Si vous utilisez
%x
ou si vous%X
envisagez six positions pour l'adresse, et si vous utilisez,%p
cela considère huit positions pour l'adresse. Par exemple:la source