web-dev-qa-db-fra.com

Corriger le spécificateur de format pour imprimer le pointeur ou l'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?

161
San

La réponse la plus simple, en supposant que les aléas et les variations de format entre les différentes plateformes ne vous gêne pas, est la notation standard %p.

La norme C99 (ISO/IEC 9899: 1999) stipule au § 7.19.6.1 paragraphe 8:

p L'argument doit être un pointeur sur 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 - l'information se trouve au § 7.21.6.1, ¶8.)

Sur certaines plates-formes, cela inclura un 0x initial et d'autres non, les lettres peuvent être en minuscule ou en majuscule, et le standard C ne précise même pas qu'il doit s'agir d'hexadécimal. sortie si je ne connais aucune implémentation où ce n'est pas.

La question de savoir si vous devez convertir explicitement les pointeurs avec une distribution (void *) est quelque peu ouverte. C'est 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 sur void'. Sur la plupart des machines, vous éviteriez de supprimer une distribution explicite. Cependant, il serait important sur une machine où la représentation en bits d’une adresse char * pour un emplacement de mémoire donné est différente de celle du pointeur () 'adresse pour le même emplacement mémoire. Ce serait une machine adressée par Word, au lieu d'une adresse adressée par 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é était l'une d'elles (ICL Perq).

Si vous n'êtes pas satisfait du comportement défini par %p de l'implémentation, utilisez C99 <inttypes.h> et 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 de placer les chiffres hexadécimaux en majuscule 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 et non comme 0xa1b2cdef qui plonge de haut en bas le long du nombre aussi. Votre choix cependant, dans de très larges limites. Le fichier (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 le casting, même si je suis sûr qu'il y en a qui 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 par défaut en standard *? Sinon, si int* était, par exemple, deux octets, et void* était de 4 octets, ce serait clairement une erreur de lire quatre octets à partir de l'argument, non?

J'avais l'illusion que le standard C indique que tous les pointeurs d'objet 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 aussi catégorique (bien que je ne sache pas d’implémentation où ce que j’ai suggéré est vrai est en réalité faux):

§6.2.5 Types

¶26 Un pointeur sur void 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 sur les 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 sur les types de structure doivent avoir les mêmes exigences de représentation et d'alignement. Tous les pointeurs sur les types d'union doivent avoir les 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 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, renvoient des valeurs de fonctions et des membres de syndicats.

(C11 dit exactement la même chose dans la section §6.2.5, paragraphe 28 et note de bas de page 48.)

Ainsi, tous les pointeurs sur des structures doivent avoir la même taille et partager les mêmes exigences d'alignement, même si les structures pointées par les pointeurs peuvent avoir des exigences d'alignement différentes. De même pour les syndicats. Les pointeurs de caractère et les pointeurs vides doivent avoir les mêmes exigences en matière de taille et d'alignement. Les pointeurs vers les variations sur int (signifiant unsigned int et signed int) doivent avoir la même taille et les mêmes exigences d’alignement; de même pour d'autres types. Mais le standard C ne dit pas formellement que sizeof(int *) == sizeof(void *). Oh bien, 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. Vous pouvez y avoir des pointeurs de données 16 bits, mais des pointeurs de fonction 32 bits, ou inversement. C'est pourquoi la norme C n'impose pas que les pointeurs de fonction puissent être convertis en pointeurs d'objet et inversement.

Heureusement (pour les programmeurs ciblant POSIX), POSIX aborde la violation et oblige les pointeurs de fonction et de données à avoir la même taille:

§2.12.3 types de pointeur

Tous les types de pointeurs de fonction doivent avoir la même représentation que le pointeur de type sur void. La conversion d'un pointeur de fonction en void * ne modifiera 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, à l'aide d'une conversion explicite, sans perte d'informations.

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

Donc, il semble que des conversions explicites sur void * soient fortement recommandées pour une fiabilité maximale du code lors du passage d'un pointeur sur une fonction variadique telle que printf(). Sur les systèmes POSIX, il est prudent de convertir un pointeur de fonction en un pointeur vide pour l’impression. Sur d’autres systèmes, il n’est pas forcément prudent de le faire, pas plus que de passer des pointeurs autres que void * sans transtypage.

201
Jonathan Leffler

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 conversion est un comportement indéfini et que l'impression avec le spécificateur de conversion p est effectuée d'une manière définie par l'implémentation.

40
ouah

Utilisez %p pour "pointeur" et n'utilisez rien d'autre *. Vous n'êtes pas garanti par le standard que vous êtes autorisé à traiter un pointeur comme un type particulier d'entier, vous obtiendrez donc un comportement non défini avec les formats intégraux. (Par exemple, %u s'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 réponse de Jonathan!] Alternativement en %p, vous pouvez utiliser des macros spécifiques au pointeur de <inttypes.h>, ajoutées à C99.

Tous les pointeurs d’objets sont implicitement convertibles en void* en C, mais pour pouvoir passer le pointeur sous la forme d’un argument variadique, vous devez le convertir explicitement (puisque les pointeurs d’objets arbitraires sont uniquement convertibles, mais pas identique aux pointeurs vides):

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

En guise d'alternative aux autres (très bonnes) réponses, vous pouvez transtyper en uintptr_t ou intptr_t (à partir de stdint.h/inttypes.h) et utiliser les spécificateurs de conversion d'entier 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 correcteurs de type.

8
R..