258 votes

Le spécificateur de format correct pour imprimer un pointeur ou une adresse ?

Quel spécificateur de format dois-je utiliser pour imprimer l'adresse d'une variable ? Je suis confus entre le lot ci-dessous.

%u - nombre entier non signé

%x - valeur hexadécimale

%p - pointeur de vide

Quel serait le format optimal pour imprimer une adresse ?

306voto

Jonathan Leffler Points 299946

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.

3 votes

Je suis un peu confus au sujet des promotions standard et des arguments variadiques. Est-ce que tous les pointeurs sont promus de façon standard 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 ?

0 votes

Notez qu'une mise à jour de la norme POSIX (POSIX 2013) a supprimé la section 2.12.3, déplaçant la plupart des exigences vers la section 2.12.4. dlsym() à la place. Un jour, j'écrirai le changement... mais "un jour" n'est pas "aujourd'hui".

0 votes

Cette réponse s'applique-t-elle également aux pointeurs vers les fonctions ? Peuvent-ils être convertis en void * ? Hmm je vois votre commentaire aquí . Puisqu'une seule conversion est nécessaire (pointeur de fonction vers void * ), cela fonctionne alors ?

75voto

ouah Points 75311

p est le spécificateur de conversion pour imprimer les pointeurs. Utilisez ceci.

int a = 42;

printf("%p\n", (void *) &a);

Rappelez-vous que l'omission de la distribution est un comportement non défini et que l'impression avec p Le spécificateur de conversion est fait d'une manière définie par l'implémentation.

3 votes

Pardon, pourquoi l'omission de la distribution est un "comportement non défini" ? Est-ce que l'adresse de la variable importe, si tout ce dont vous avez besoin est l'adresse, pas la valeur ?

11 votes

@valdo parce que C le dit (C99, 7.19.6.1p8) "p L'argument doit être un pointeur vers void".

14 votes

@valdo : Ce n'est pas nécessairement le cas que tous les pointeurs ont la même taille / représentation.

37voto

Kerrek SB Points 194696

Utilice %p pour "pointeur", et n'utilisez rien d'autre*. La norme ne vous garantit pas que vous êtes autorisé à traiter un pointeur comme un type particulier d'entier, donc vous obtiendrez un comportement non défini avec les formats intégraux. (Par exemple, %u attend un unsigned int mais que se passe-t-il si void* a une exigence de taille ou d'alignement différente de celle de unsigned int ?)

*) [Voir l'excellente réponse de Jonathan !] Autre solution pour %p vous puede utiliser les macros spécifiques aux pointeurs de <inttypes.h> ajouté en C99.

Tous les pointeurs d'objets sont implicitement convertibles en void* en C, mais pour passer le pointeur en tant qu'argument variadique, vous devez le caster explicitement (puisque les pointeurs d'objets arbitraires ne sont que des pointeurs de type convertible mais pas identique à des pointeurs vides) :

printf("x lives at %p.\n", (void*)&x);

3 votes

Tous objet Les pointeurs sont convertibles en void * (bien que pour printf() vous avez techniquement besoin du cast explicite, puisque c'est une fonction variadique). Les pointeurs de fonction ne sont pas nécessairement convertibles en void * .

0 votes

@caf : Oh, je ne savais pas pour les arguments variadiques - corrigé ! Merci !

2 votes

Le standard C n'exige pas que les pointeurs de fonction soient convertibles en void * et de retour en pointeur de fonction sans perte ; heureusement, POSIX l'exige explicitement (en notant que cela ne fait pas partie du C standard). Donc, dans la pratique, vous pouvez vous en tirer sans problème (en convertissant void (*function)(void) a void * et de retour à void (*function)(void) ), mais ce n'est pas strictement imposé par la norme C.

9voto

R.. Points 93718

Comme alternative aux autres (très bonnes) réponses, vous pourriez lancer à uintptr_t o intptr_t (de stdint.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 tenue de fournir ces typedefs.

0 votes

Envisager #include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); } Est-ce un comportement non défini que d'imprimer l'adresse d'une variable en utilisant la fonction %u spécificateur de format ? Dans la plupart des cas, l'adresse de la variable est positive, je peux donc utiliser la fonction %u au lieu de %p ?

1 votes

@Destructeur : Non, %u est un format pour unsigned int et ne peut pas être utilisé avec un argument de type pointeur à l'adresse printf .

6voto

Pasha Points 25

Vous pouvez utiliser %x o %X o %p ; tous sont corrects.

  • Si vous utilisez %x l'adresse est indiquée en minuscules, par exemple : a3bfbc4
  • Si vous utilisez %X l'adresse est indiquée en majuscules, par exemple : A3BFBC4

Les deux sont corrects.

Si vous utilisez %x o %X il considère six positions pour l'adresse, et si vous utilisez %p il envisage huit postes pour l'adresse. Par exemple :

2 votes

Bienvenue sur SO. Prenez le temps de lire les autres réponses, elles expliquent clairement un certain nombre de détails qui vous échappent.

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