2012-01-29 17 views

risposta

133

la risposta più semplice, a patto che non si mente i capricci e le variazioni in formato tra diverse piattaforme, è il %p notazione standard.

Lo standard C99 (ISO/IEC 9899: 1999) dice nel §7.19.6.1 ¶8:

p L'argomento deve essere un puntatore a void. Il valore del puntatore è convertito in una sequenza di caratteri di stampa, in un modo definito dall'implementazione.

(In C11 - ISO/IEC 9899: 2011 - le informazioni sono in §7.21.6.1 ¶8.)

Su alcune piattaforme, che includerà un leader 0x e sugli altri non lo farà e le lettere potrebbero essere minuscole o maiuscole, e lo standard C non definisce nemmeno l'output esadecimale, sebbene non conosca un'implementazione laddove non lo è.

È un po 'aperto discutere se è necessario convertire esplicitamente i puntatori con un cast (void *).È esplicito, che di solito è buono (quindi è quello che faccio), e lo standard dice "l'argomento deve essere un puntatore a void". Sulla maggior parte delle macchine, si eviterà di omettere un cast esplicito. Tuttavia, sarebbe importante su una macchina in cui la rappresentazione bit di un indirizzo per una data posizione di memoria fosse diversa dall'indirizzo "altro puntatore" per la stessa posizione di memoria. Questa sarebbe una macchina indirizzata alla parola, anziché a byte. Queste macchine non sono comuni (probabilmente non disponibili) al giorno d'oggi, ma la prima macchina su cui ho lavorato dopo l'università era una di queste (ICL Perq).

Se non siete soddisfatti del comportamento definito dall'implementazione di %p, quindi utilizzare C99 <inttypes.h> e uintptr_t invece:

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

Questo ti permette di ottimizzare la rappresentazione per soddisfare se stessi. Ho scelto di avere le cifre esadecimali in maiuscolo in modo che il numero sia uniformemente uguale alla stessa altezza e la curva caratteristica all'inizio di 0xA1B2CDEF appaia così, non come 0xa1b2cdef che si immerge anche su e giù lungo il numero. La tua scelta però, entro limiti molto ampi. Il cast (uintptr_t) è consigliato senza ambiguità da GCC quando può leggere la stringa di formato in fase di compilazione. Penso che sia corretto richiedere il cast, anche se sono sicuro che ci sono alcuni che ignorerebbero l'avvertimento e farla franca la maggior parte del tempo.


Kerrek chiede nei commenti:

Sono un po 'confuso su promozioni standard e argomenti variadic. Tutti i puntatori vengono promossi con la promozione standard *? Altrimenti, se int* fossero, ad esempio, due byte e void* fossero 4 byte, sarebbe chiaramente un errore leggere quattro byte dall'argomento, non?

Ho avuto l'illusione che lo standard C dice che tutti i puntatori oggetto deve essere la stessa dimensione, in modo da void * e int * non possono essere di dimensioni diverse. Tuttavia, quello che penso è la relativa sezione dello standard C99 non è così enfatico (anche se non so di un'implementazione in cui quello che ho suggerito è vero in realtà è falso):

§6.2.5 Tipi

¶26 Un puntatore a vuoto deve avere gli stessi requisiti di rappresentazione e allineamento di un puntatore a un tipo di carattere. 39) Analogamente, i riferimenti a versioni qualificate o non qualificate di tipi compatibili devono avere gli stessi requisiti di rappresentazione e allineamento. Tutti i puntatori ai tipi di strutture devono avere gli stessi requisiti di rappresentazione e allineamento reciproci. Tutti i puntatori ai tipi di unione devono avere gli stessi requisiti di rappresentazione e allineamento reciproci. I puntatori ad altri tipi non devono avere la stessa rappresentazione o requisiti di allineamento.

39) Gli stessi requisiti di rappresentazione e allineamento intendono implicare l'intercambiabilità come argomenti delle funzioni, valori di ritorno da funzioni e membri di unioni.

(C11 dice esattamente lo stesso nella sezione §6.2.5, ¶28 e nota 48.

Quindi, tutti i puntatori alle strutture devono essere della stessa dimensione l'uno dell'altro e devono condividere gli stessi requisiti di allineamento, anche se le strutture a cui puntano i puntatori possono avere requisiti di allineamento diversi. Allo stesso modo per i sindacati. Puntatori di caratteri e puntatori di vuoti devono avere le stesse dimensioni e requisiti di allineamento. Puntatori a variazioni su int (ovvero unsigned int e signed int) devono avere le stesse dimensioni e requisiti di allineamento reciproci; allo stesso modo per altri tipi. Ma lo standard C non dice formalmente che sizeof(int *) == sizeof(void *). Oh bene, SO è buono per farti ispezionare le tue ipotesi.

Lo standard C in modo definitivo non richiede che i puntatori di funzione abbiano le stesse dimensioni dei puntatori di oggetto. Era necessario non rompere i diversi modelli di memoria su sistemi DOS-like. Ci si potrebbe avere puntatori di dati a 16 bit ma puntatori di funzione a 32 bit, o viceversa. Questo è il motivo per cui lo standard C non impone che i puntatori di funzione possano essere convertiti in puntatori di oggetti e viceversa.

Fortunatamente (per i programmatori destinati a POSIX), POSIX passi sulla breccia e fa mandato che puntatori a funzione e puntatori di dati sono le stesse dimensioni:

§2.12.3 Pointer Types

Tutti i tipi di puntatore a funzione deve avere la stessa rappresentazione del puntatore di tipo a void. La conversione di un puntatore a void * non modifica la rappresentazione. Un valore void * risultante da tale conversione può essere riconvertito al tipo di puntatore a funzione originale, utilizzando un cast esplicito, senza perdita di informazioni.

Nota: Lo standard ISO C non richiede questo, ma è necessario per la conformità POSIX.

Così, sembra che cast espliciti a void * sono fortemente consigliabile per la massima affidabilità nel codice quando si passa un puntatore a una funzione variadic come printf(). Sui sistemi POSIX, è sicuro lanciare un puntatore a funzione su un puntatore vuoto per la stampa. Su altri sistemi, non è necessariamente sicuro farlo, né è necessariamente sicuro passare puntatori diversi da void * senza un cast.

+3

Sono un po 'confuso riguardo le promozioni standard e gli argomenti variadici. Tutti i puntatori vengono promossi standard a 'void *'? Altrimenti, se 'int *' erano, diciamo, due byte e 'void *' erano 4 byte, allora sarebbe chiaramente un errore leggere quattro byte dall'argomento, non? –

+0

Si noti che un aggiornamento di POSIX (POSIX 2013) ha rimosso la sezione 2.12.3, spostando la maggior parte dei requisiti nel ['dlsym()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym .html) invece. Un giorno scriverò il cambiamento ... ma "un giorno" non è "oggi". –

+0

Questa risposta si applica anche ai puntatori alle funzioni? Possono essere convertiti in 'void *'? Hmm Vedo il tuo commento [qui] (http://stackoverflow.com/questions/9053658/correct-format-specifier-to-print-pointer-address#comment11360489_9053677). Poiché è necessaria solo una conversione da un wat (puntatore a 'void *'), allora funziona? – chux

28

p è lo specificatore di conversione per stampare i puntatori. Usa questo.

int a = 42; 

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

Ricordate che omettendo il cast è un comportamento indefinito e che la stampa con p specificatore di conversione è fatta in un modo definito dall'implementazione.

+0

Scusa, perché omettere il cast è "comportamento indefinito"? Questo è l'indirizzo di quale variabile è, se tutto ciò di cui hai bisogno è l'indirizzo, non il valore? – valdo

+8

@valdo perché C lo dice (C99, 7.19.6.1p8) "p L'argomento deve essere un puntatore a vuoto." – ouah

+9

@valdo: non è necessariamente il caso che tutti i puntatori abbiano la stessa dimensione/rappresentazione. – caf

21

Utilizzare %p, per "puntatore" e non utilizzare altro *. Non sei garantito dallo standard che ti è permesso trattare un puntatore come un particolare tipo di numero intero, in modo da ottenere un comportamento indefinito con i formati integrati. (Per esempio, %u si aspetta un unsigned int, ma cosa succede se void* ha una dimensione diversa o un requisito di allineamento rispetto unsigned int?)

*) [Si veda la risposta multa di Jonathan!] In alternativa al %p, voi can uso specifici puntatore macro da <inttypes.h>, aggiunto in C99.

Tutti i puntatori di oggetti sono implicitamente convertibile in void* in C, ma per passare il puntatore come argomento variadic, si deve lanciare in modo esplicito (dal momento che i puntatori di oggetti arbitrari sono solo convertibili, ma non identico a puntatori nulli):

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

Tutti i puntatori * object * sono convertibili in 'void *' (sebbene per 'printf()' è tecnicamente necessario il cast esplicito, poiché è una funzione variadica). I puntatori di funzione non sono necessariamente convertibili in 'void *'. – caf

+0

@caf: Oh, non sapevo degli argomenti variadici - risolto! Grazie! –

+2

Lo standard C non richiede che i puntatori di funzione siano convertibili in 'void *' e tornino al puntatore di funzione senza perdita; fortunatamente, però, POSIX richiede esplicitamente che (notando che non fa parte dello standard C). Quindi, in pratica, puoi farla franca (convertendo 'void (* function) (void)' a 'void *' e ritorna a 'void (* function) (void)'), ma rigorosamente non è richiesto da lo standard C –

8

Come alternativa agli altri (molto buono) risposte, si potrebbe cast uintptr_t o intptr_t (da stdint.h/inttypes.h) e utilizzare i corrispondenti specificatori di conversione intero. Ciò consentirebbe una maggiore flessibilità nel modo in cui il puntatore viene formattato, ma in senso stretto non è richiesta un'implementazione per fornire questi typedef.

+0

Bello, non sapeva di 'uintptr_t'. – cnicutar

+0

considera '#include int main (void) {int p = 9; int * m = & s; printf ("% u", m); } __è un comportamento non definito per stampare l'indirizzo della variabile usando l'identificatore di formato '% u'? __ L'indirizzo della variabile nella maggior parte dei casi è positivo, quindi posso usare'% u' invece di '% p'? – Destructor

+0

@Destructor: No, '% u' è un formato per il tipo' unsigned int' e non può essere utilizzato con un argomento pointer su 'printf'. –

Problemi correlati