In una domanda recente, qualcuno ha detto che quando si stampa un valore di puntatore con printf, il chiamante deve lanciare il puntatore a void *, in questo modo:
int *my_ptr = .... printf("My pointer is: %p", (void *)my_ptr);
Per la vita di me non riesco a capire perché. Ho trovato questa domanda , che è quasi la stessa. La risposta alla domanda è corretta – spiega che gli inte e i puntatori non hanno necessariamente la stessa lunghezza.
Questo è, ovviamente, vero, ma quando ho già un puntatore, come nel caso precedente, perché dovrei trasmettere da int *
a void *
? Quando è un * int diverso dal vuoto *? In effetti, quando fa (void *)my_ptr
genera un codice macchina diverso da my_ptr
?
AGGIORNAMENTO: i risponditori multipli esperti hanno citato lo standard, dicendo che passare il tipo sbagliato potrebbe comportare un comportamento indefinito. Come? Prevedo printf("%p", (int *)ptr)
e printf("%p", (void *)ptr)
per generare lo stesso identico stack frame. Quando le due chiamate genereranno diversi stack frame?
Nel linguaggio C tutti i tipi di puntatore possono differire nelle loro rappresentazioni. Quindi, sì, int *
è diverso da void *
. Una piattaforma di vita reale che illustrerebbe questa differenza potrebbe essere difficile (o imansible) da trovare, ma a livello concettuale la differenza è ancora lì.
In altre parole, in generale i diversi tipi di puntatore hanno rappresentazioni diverse. int *
è diverso da void *
e diverso da double *
. Il fatto che la tua piattaforma usi la stessa rappresentazione per void *
e int *
non è altro che una coincidenza, per quanto riguarda il linguaggio C.
Il linguaggio afferma che alcuni tipi di puntatore devono avere rappresentazioni identiche, che include void *
contro char *
, puntatori a diversi tipi di struct o, per esempio, int *
e const int *
. Ma queste sono solo eccezioni dalla regola generale.
p
specificatore di conversione p
richiede un argomento di tipo void *
. Se non si passa un argomento di tipo void *
, la chiamata alla funzione richiama il comportamento non definito.
Dallo standard C:
(C11, 7.21.6.1p8 Funzioni di input / output formattate) “p L’argomento deve essere un puntatore a vuoto.”
Non è necessario che i tipi di puntatore in C abbiano la stessa dimensione o la stessa rappresentazione.
Un esempio di implementazione con rappresentazione di tipi di puntatori diversi è Cray PVP in cui la rappresentazione dei tipi di puntatore è 64-bit per void *
e char *
ma 32-bit per gli altri tipi di puntatore.
Vedi “Manuale di riferimento Cray C / C ++”, Tabella 3. in “9.1.2.2” http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf
In realtà, ad eccezione di antichi mainframe / minis, è molto improbabile che diversi tipi di puntatori abbiano dimensioni diverse. Tuttavia hanno tipi diversi, e secondo le specifiche per printf
, chiamandolo con l’argomento di tipo errato per lo specificatore di formato risulta in un comportamento indefinito . Questo significa che non farlo.
p
L’argomento deve essere un puntatore avoid
. Il valore del puntatore viene convertito in una sequenza di caratteri di stampa, in un modo definito dall’implementazione.
Altre persone hanno affrontato adeguatamente il caso di passare un int *
a una funzione prototipata con un numero fisso di argomenti che si aspetta un diverso tipo di puntatore.
printf
non è una funzione del genere. È una funzione variadica, quindi le promozioni degli argomenti predefiniti vengono utilizzate per i relativi argomenti anonimi (ovvero tutto dopo la stringa di formato) e se il tipo promosso di ciascun argomento non corrisponde esattamente al tipo previsto dall’effector di formattazione, il comportamento non è definito. In particolare, anche se int *
e void *
hanno una rappresentazione identica,
int a; printf("%p\n", &a);
ha un comportamento indefinito.
Questo perché il layout del frame di chiamata può dipendere dal tipo concreto esatto di ciascun argomento. Gli ABI che specificano aree argomento diverse per i tipi di puntatori e non puntatori si sono verificati nella vita reale (ad esempio, il Motorola 68000 vorrebbe mantenere i puntatori nei registri indirizzi e non puntatori nei registri dati nella misura massima ansible). Non sono a conoscenza di alcun ABI del mondo reale che segrega tipi di puntatore distinti, ma è permesso e non mi sorprenderebbe sentirne parlare.