Corriger le spécificateur de format pour imprimer le pointeur (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 – Entier non signé

% x – valeur hexadécimale

% p – pointeur vide

Quel serait le format optimal pour imprimer une adresse?

La réponse la plus simple, en supposant que les caprices et les variations de format entre les différentes plates-formes ne vous dérangent pas, est la notation standard %p .

La norme C99 (ISO / IEC 9899: 1999) indique au §7.1.6.6.1 ¶8:

p L’argument doit être un pointeur à void . La valeur du pointeur est convertie en une séquence de caractères d’impression, de manière définie par l’implémentation.

(Dans C11 – ISO / IEC 9899: 2011 – les informations sont au § 7.21.6.1 ¶8.)

Sur certaines plates-formes, cela inclura un 0x et d’autres non, et les lettres pourraient être en minuscule ou en majuscule, et le standard C ne définit même pas qu’il doit s’agir d’une sortie hexadécimale bien que je sache de pas d’implémentation où ce n’est pas.

Il est un peu ouvert de débattre si vous devez convertir explicitement les pointeurs avec un cast (void *) . Il est explicite, ce qui est généralement bon (donc c’est ce que je fais), et la norme dit «l’argument doit être un pointeur vers un void ». Sur la plupart des machines, vous éviteriez une dissortingbution explicite. Cependant, il serait important sur une machine où la représentation binary d’une adresse de type char * pour un emplacement de mémoire donné est différente de l’adresse de « tout autre point » pour le même emplacement de mémoire. Ce serait une machine adressée par mot, au lieu d’être adressée par octet. De telles machines ne sont pas communes (probablement pas disponibles) de nos jours, mais la première machine sur laquelle j’ai travaillé après l’université était une telle (ICL Perq).

Si vous n’êtes pas satisfait du comportement de %p défini par l’implémentation, utilisez C99 et uintptr_t place:

 printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer); 

Cela vous permet d’affiner la représentation selon vos besoins. J’ai choisi d’avoir les chiffres hexadécimaux en majuscule pour que le nombre soit uniformément à la même hauteur et la caractéristique au début de 0xA1B2CDEF apparaisse donc, pas comme 0xa1b2cdef qui plonge aussi le long du nombre. Votre choix cependant, dans des limites très larges. Le (uintptr_t) 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 la dissortingbution, 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 annulés *? Sinon, si int* était, disons, deux octets, et void* 4 octets, alors ce serait clairement une erreur de lire quatre octets de l’argument, non?

J’étais sous l’illusion que le standard C dit que tous les pointeurs d’object doivent avoir 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 évidente (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 à annuler doit avoir les mêmes exigences de représentation et d’alignement qu’un pointeur sur 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 les types de structure doivent avoir les mêmes exigences de représentation et d’alignement les uns des autres. Tous les pointeurs vers les types d’union doivent avoir les mêmes exigences de représentation et d’alignement les uns des 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 impliquent l’interchangeabilité en tant qu’arguments de fonctions, de valeurs de retour de fonctions et de 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 les structures doivent avoir la même taille et doivent respecter les mêmes exigences d’alignement, même si les structures sur 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 de vide doivent avoir la même taille et les mêmes exigences d’alignement. Les pointeurs vers les variantes sur int (ce qui signifie que les éléments unsigned int et signed int ) doivent avoir la même taille et les mêmes exigences d’alignement les uns que les autres; de même pour les autres types. Mais le standard C ne dit pas formellement que sizeof(int *) == sizeof(void *) . Eh bien, SO est bon pour vous faire inspecter vos hypothèses.

La norme C ne requirejs pas forcément que les pointeurs de fonction aient la même taille que les pointeurs d’object. Cela était nécessaire pour ne pas casser les différents modèles de mémoire sur les systèmes de type DOS. Vous pouvez y trouver des pointeurs de données 16 bits, mais des pointeurs de fonctions 32 bits, ou vice versa. C’est pourquoi le standard C n’impose pas que les pointeurs de fonctions puissent être convertis en pointeurs d’object et inversement.

Heureusement (pour les programmeurs ciblant POSIX), POSIX entre dans la brèche et exige 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 pointeur de type à annuler. La conversion d’un pointeur de fonction en void * ne modifie pas la représentation. Une valeur void * résultant d’une telle conversion peut être reconvertie dans le type de pointeur de fonction d’origine, en utilisant une conversion explicite, sans perte d’informations.

Remarque: La norme ISO C ne l’exige pas, mais elle est requirejse pour la conformité POSIX.

Donc, il semble que les jets explicites à void * sont fortement recommandés pour une fiabilité maximale du code lors du passage d’un pointeur à une fonction variad, telle que printf() . Sur les systèmes POSIX, il est prudent de convertir un pointeur de fonction en pointeur vide pour l’imprimer. Sur d’autres systèmes, il n’est pas nécessairement prudent de le faire, et il n’est pas nécessairement sûr de faire passer des pointeurs autres que void * sans dissortingbution.

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

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

Rappelez-vous que l’omission de la dissortingbution est un comportement indéfini et que l’impression avec un spécificateur de conversion p se fait d’une manière définie par l’implémentation.

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

*) [Voir la bonne réponse de Jonathan!] Alternativement à %p , vous pouvez utiliser des macros spécifiques au pointeur de , ajoutées dans C99.

Tous les pointeurs d’object sont implicitement convertibles en void* dans C, mais pour passer le pointeur comme un argument variadic, vous devez le convertir explicitement (puisque les pointeurs d’object arbitraires sont uniquement convertibles , mais pas identiques aux pointeurs de vide):

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

En uintptr_t alternative aux autres réponses (très bonnes), vous pouvez lancer uintptr_t ou intptr_t (à partir 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 requirejse pour fournir ces typedefs.