Qual è il corretto uso di printf per visualizzare i puntatori riempiti con 0s
Domanda
In C, mi piacerebbe usare printf per visualizzare i puntatori, e in modo che siano allineati correttamente, mi piacerebbe loro pad con 0s.
La mia ipotesi è che il modo corretto di fare questo è stato:
printf("%016p", ptr);
Questo funziona, ma questo gcc si lamenta con il seguente messaggio:
warning: '0' flag used with ‘%p’ gnu_printf format
Googled un po 'per questo, e il thread è sullo stesso argomento, ma non davvero dare una soluzione.
http://gcc.gnu.org/ml/ gcc-bugs / 2003-05 / msg00484.html
Leggendolo, sembra che il motivo per cui gcc si lamenta è che la sintassi ho suggerito non è definito nella C99. Ma io non riesco a trovare un altro modo per fare la stessa cosa in un modo standard approvato.
Così qui è la doppia domanda:
- È la mia comprensione corretta che questo comportamento non è definito dallo standard C99?
- Se è così, c'è una norma approvata, modo portabile di fare questo?
Soluzione
#include <inttypes.h>
#include <stdint.h>
printf("%016" PRIxPTR "\n", (uintptr_t)ptr);
, ma non stamperà il puntatore nel modo definito implementazione (dice DEAD:BEEF
per 8086 modo segmentato).
Altri suggerimenti
Usa:
#include <inttypes.h>
printf("0x%016" PRIXPTR "\n", (uintptr_t) pointer);
In alternativa, utilizzare un'altra variante delle macro da quel colpo di testa.
Si noti inoltre che alcune implementazioni di printf()
stampare un '0x
' davanti al puntatore; altri non lo fanno (ed entrambi sono corretti secondo lo standard C).
L'unico portatile modo per stampare i valori dei puntatori è utilizzare lo specificatore "%p"
, che produce in uscita implementazione definita. (Conversione in uintptr_t
e utilizzando PRIxPTR
è probabile che il lavoro, ma (a) uintptr_t
è stato introdotto nel C99, in modo da alcuni compilatori non può sostenerlo, e (b) non è garantito ad esistere anche in C99 (ci potrebbe non essere un tipo integer abbastanza grande da contenere tutti i possibili valori di puntatore.
Così utilizzare "%p"
con sprintf()
(o snprintf()
, se lo avete) per stampare su una stringa, e quindi stampare l'imbottitura stringa con spazi vuoti iniziali.
Un problema è che non c'è davvero un buon modo per determinare la lunghezza massima della stringa prodotta da "%p"
.
Questa è certamente scomodo (anche se naturalmente si può avvolgere in una funzione). Se non avete bisogno di portabilità al 100%, non ho mai usato un sistema sono stati gettando il puntatore unsigned long
e stampare il risultato usando "0x%16lx"
non avrebbe funzionato. [ Aggiorna : Sì, ho. 64-bit di Windows ha i puntatori a 64 bit e 32 bit unsigned long
.] (Oppure è possibile utilizzare "0x%*lx"
e calcolare la larghezza, probabilmente qualcosa come sizeof (void*) * 2
. Se sei veramente paranoico, è possibile tenere conto di sistemi in cui CHAR_BIT > 8
, in modo da' ll ottenere più di 2 cifre esadecimali per byte.)
Questa risposta è simile a quello dato in precedenza in https://stackoverflow.com/a/1255185/1905491 ma prende anche le larghezze possibili in considerazione (come indicato da https://stackoverflow.com/a/6904396/1905491 che non riconobbi finché la mia risposta è stata resa sotto di esso;). Quanto segue è stato tagliato stamperà puntatori come 8 caratteri esadecimali 0-passati su macchine sane * dove possono essere rappresentati da 32 bit, 16 su 64b e 16b 4 su sistemi.
#include <inttypes.h>
#define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))
printf("0x%0*" PRIxPTR, PRIxPTR_WIDTH, (uintptr_t)pointer);
Si noti l'uso del carattere asterisco per andare a prendere la larghezza con l'argomento successivo, che è in C99 (probabilmente prima?), Ma che è abbastanza raramente visto "in the wild". Questo è il modo migliore di usare la conversione p
perché quest'ultimo è definito dall'implementazione.
* Lo standard consente uintptr_t
ad essere più grande rispetto al minimo, ma suppongo non ci sono implementazioni che non utilizza il minimo.
E 'facile da risolvere se si esegue il cast il puntatore ad un tipo lungo. Il problema è questo non funziona su tutte le piattaforme assume una puntatore può essere contenuto in una lunga, e che un puntatore può essere visualizzato in 16 caratteri (64 bit). Questo è comunque vero sulla maggior parte delle piattaforme.
in modo che sarebbe diventato:
printf("%016lx", (unsigned long) ptr);
Come il tuo link suggerisce già, il comportamento è indefinito. Non credo ci sia un modo di fare questo portatile come% p stessa dipende dalla piattaforma su cui state lavorando. Si potrebbe naturalmente solo il cast del puntatore a un int e visualizzarli in esadecimale:
printf("0x%016lx", (unsigned long)ptr);
Forse questo sarà interessante (da una macchina Windows a 32 bit, usando 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
Come si può vedere, le pastiglie versione% p con zeri alla dimensione di un puntatore, e poi spazi alla dimensione specificata, mentre utilizzando% x e calchi (i calchi e di formato sono altamente portabile) utilizza solo zeri.
Si noti che se le stampe puntatore con il prefisso "0x", si avrebbe bisogno di sostituire "% 018p" per compensare.
Io di solito uso% x per visualizzare i puntatori. Suppongo che non è portabile su sistemi a 64 bit, ma funziona bene per 32-amari.
Sarò interessato a vedere quali risposte ci sono per soluzioni portatili , dal momento che la rappresentazione puntatore non è esattamente portatile.