web-dev-qa-db-fra.com

Quelle est l'utilisation appropriée de printf pour afficher des pointeurs remplis de 0

En C, j'aimerais utiliser printf pour afficher les pointeurs, et pour qu'ils s'alignent correctement, je voudrais les remplir avec des 0.

Je suppose que la bonne façon de procéder était:

printf ("% 016p", ptr);

Cela fonctionne, mais ce gcc se plaint du message suivant:

avertissement: indicateur "0" utilisé avec le format "% p" gnu_printf

J'ai googlé un peu pour cela, et le fil suivant est sur le même sujet, mais ne donne pas vraiment de solution.

http://gcc.gnu.org/ml/gcc-bugs/2003-05/msg00484.html

En le lisant, il semble que la raison pour laquelle gcc se plaint est que la syntaxe que j'ai suggérée n'est pas définie en C99. Mais je n'arrive pas à trouver d'autre moyen de faire la même chose d'une manière standard approuvée.

Voici donc la double question:

  • Ma compréhension est-elle correcte que ce comportement n'est pas défini par la norme C99?
  • Dans l'affirmative, existe-t-il un moyen standard approuvé et portable de procéder?
42
Florian

#include <inttypes.h>

#include <stdint.h>

printf("%016" PRIxPTR "\n", (uintptr_t)ptr);

mais il n'imprimera pas le pointeur de la manière définie par l'implémentation (dit DEAD:BEEF pour le mode segmenté 8086).

38
AProgrammer

Utilisation:

#include <inttypes.h>

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

Ou utilisez une autre variante des macros de cet en-tête.

Notez également que certaines implémentations de printf() affichent un '0x' Devant le pointeur; d'autres non (et les deux sont corrects selon la norme C).

8
Jonathan Leffler

La seule façon portable d'imprimer les valeurs du pointeur est d'utiliser le spécificateur "%p", Qui produit une sortie définie par l'implémentation. (La conversion en uintptr_t Et l'utilisation de PRIxPTR est susceptible de fonctionner, mais (a) uintptr_t A été introduit en C99, donc certains compilateurs peuvent ne pas le prendre en charge, et (b) c'est pas garanti d'exister même en C99 (il pourrait ne pas y avoir un type entier suffisamment grand pour contenir toutes les valeurs de pointeur possibles.

Utilisez donc "%p" Avec sprintf() (ou snprintf(), si vous l'avez) pour imprimer sur une chaîne, puis imprimez le remplissage de la chaîne avec des blancs de début.

Un problème est qu'il n'y a pas vraiment de bon moyen de déterminer la longueur maximale de la chaîne produite par "%p".

Cela est certes gênant (bien que vous puissiez bien sûr l'envelopper dans une fonction). Si vous n'avez pas besoin d'une portabilité à 100%, je n'ai jamais utilisé de système pour convertir le pointeur en unsigned long Et imprimer le résultat en utilisant "0x%16lx" Ne fonctionnerait pas. [ MISE À JOUR : Oui, je l'ai. Windows 64 bits possède des pointeurs 64 bits et 32 ​​$ unsigned long.] (Ou vous pouvez utiliser "0x%*lx" Et calculer la largeur, probablement quelque chose comme sizeof (void*) * 2. Si vous êtes vraiment paranoïaque, vous pouvez prendre en compte les systèmes où CHAR_BIT > 8, vous obtiendrez donc plus de 2 chiffres hexadécimaux par octet.)

5
Keith Thompson

Cette réponse est similaire à celle donnée précédemment dans https://stackoverflow.com/a/1255185/1905491 mais prend également en compte les largeurs possibles (comme indiqué par https: // stackoverflow.com/a/6904396/1905491 que je n'ai pas reconnu jusqu'à ce que ma réponse soit rendue en dessous;). Les extraits suivants imprimeront des pointeurs sous la forme de 8 caractères hexadécimaux passés sur 0 sur des machines sane * où ils peuvent être représentés par des systèmes 32 bits, 16 sur 64b et 4 sur 16b.

#include <inttypes.h>
#define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))

printf("0x%0*" PRIxPTR, PRIxPTR_WIDTH, (uintptr_t)pointer);

Notez l'utilisation du caractère astérisque pour récupérer la largeur par l'argument suivant, qui est en C99 (probablement avant?), Mais qui est assez rarement vu "à l'état sauvage". C'est bien mieux que d'utiliser la conversion p car cette dernière est définie par l'implémentation.

* La norme permet uintptr_t pour être plus grand que le minimum, mais je suppose qu'il n'y a aucune implémentation qui n'utilise pas le minimum.

4
stefanct

Ce sera peut-être intéressant (à partir d'une machine Windows 32 bits, en utilisant mingw):

rjeq@RJEQXPD /u
$ cat test.c
#include <stdio.h>

int main()
{
    char c;

    printf("p: %016p\n", &c);
    printf("x: %016llx\n", (unsigned long long) (unsigned long) &c);

    return 0;
}

rjeq@RJEQXPD /u
$ gcc -Wall -o test test.c
test.c: In function `main':
test.c:7: warning: `0' flag used with `%p' printf format

rjeq@RJEQXPD /u
$ ./test.exe
p:         0022FF77
x: 000000000022ff77

Comme vous pouvez le voir, les versions de% p contiennent des zéros à la taille d'un pointeur, puis des espaces à la taille spécifiée, tandis que l'utilisation de% x et des transtypages (les transtypages et le spécificateur de format sont hautement non transférables) n'utilise que des zéros.

0
Rodrigo Queiro

Notez que si le pointeur s'imprime avec le préfixe "0x", vous devrez remplacer "% 018p" pour compenser.

0
Jerry Miller

Il est facile à résoudre si vous convertissez le pointeur en un type long. Le problème est que cela ne fonctionnera pas sur toutes les plates-formes car il suppose qu'un pointeur peut tenir dans un long et qu'un pointeur peut être affiché en 16 caractères (64 bits). Cela est cependant vrai sur la plupart des plateformes.

il deviendrait donc:

printf("%016lx", (unsigned long) ptr);
0
AnthonyLambert

Comme votre lien le suggère déjà, le comportement n'est pas défini. Je ne pense pas qu'il existe une façon portable de le faire car% p lui-même dépend de la plate-forme sur laquelle vous travaillez. Vous pouvez bien sûr simplement placer le pointeur sur un entier et l'afficher en hexadécimal:

printf("0x%016lx", (unsigned long)ptr);
0
groovingandi