Che cos'è uintptr_t
ea cosa può servire?
Che cos'è uintptr_t
ea cosa può servire?
Risposte:
uintptr_t
è un tipo intero senza segno in grado di memorizzare un puntatore ai dati. Ciò significa in genere che ha le stesse dimensioni di un puntatore.
È facoltativamente definito in C ++ 11 e versioni successive.
Un motivo comune per volere un tipo intero che può contenere il tipo di puntatore di un'architettura è quello di eseguire operazioni specifiche di un intero su un puntatore o di oscurare il tipo di puntatore fornendolo come "handle" intero.
Modifica: Nota che Steve Jessop ha alcuni dettagli aggiuntivi molto interessanti (che non ruberò) in un'altra risposta qui per i tuoi tipi pedanti :)
size_t
deve essere sufficiente per contenere la dimensione dell'oggetto più grande e può essere più piccolo di un puntatore. Questo sarebbe prevedibile per architetture segmentate come l'8086 (16 bit size_t
, ma 32 bit void*
)
ptrdiff_t
. uintptr_t
non è pensato per questo.
unsigned int
solito non è abbastanza grande. Ma potrebbe essere abbastanza grande. Questo tipo esiste specificamente per rimuovere tutto "assumendo" .
La prima cosa, al momento della domanda, uintptr_t
non era in C ++. È in C99, in <stdint.h>
, come tipo opzionale. Molti compilatori C ++ 03 forniscono quel file. È anche in C ++ 11, in <cstdint>
, dove di nuovo è facoltativo e che fa riferimento a C99 per la definizione.
In C99, è definito come "un tipo intero senza segno con la proprietà che qualsiasi puntatore valido a vuoto può essere convertito in questo tipo, quindi riconvertito in puntatore a vuoto e il risultato comparerà uguale al puntatore originale".
Prendi questo per dire ciò che dice. Non dice nulla sulla dimensione.
uintptr_t
potrebbe avere le stesse dimensioni di a void*
. Potrebbe essere più grande. Probabilmente potrebbe essere più piccolo, sebbene tale implementazione del C ++ si avvicini perverso. Ad esempio su una piattaforma ipotetica in cui void*
sono presenti 32 bit, ma vengono utilizzati solo 24 bit di spazio di indirizzi virtuali, è possibile disporre di un 24 bit uintptr_t
che soddisfi i requisiti. Non so perché un'implementazione lo farebbe, ma lo standard lo consente.
void*
. Tuttavia, influisce su possibili direzioni future, soprattutto se si potrebbe voler cambiare per usare qualcosa che in realtà è solo un handle intero, non un puntatore convertito.
typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;
. Invece: callback.dataPtr = (void*)val
. D'altra parte, ovviamente ottieni void*
e devi ricollegarlo int
.
È un tipo intero senza segno esattamente la dimensione di un puntatore. Ogni volta che devi fare qualcosa di insolito con un puntatore, come ad esempio capovolgi tutti i bit (non chiedere perché) lo casti uintptr_t
e lo manipoli come un normale numero intero, quindi esegui nuovamente il cast.
void*
valore del puntatore in uintptr_t
e viceversa produce un void*
valore che è uguale al puntatore originale. uintptr_t
di solito ha le stesse dimensioni di void*
, ma non è garantito, né esiste alcuna garanzia che i bit del valore convertito abbiano un significato particolare. E non vi è alcuna garanzia che possa contenere un valore da puntatore a funzione convertito senza perdita di informazioni. Infine, non è garantito che esista.
Esistono già molte risposte valide alla parte "cos'è il tipo di dati uintptr_t". Proverò ad affrontare il "a cosa serve?" parte in questo post.
Principalmente per operazioni bit a bit sui puntatori. Ricorda che in C ++ non è possibile eseguire operazioni bit a bit sui puntatori. Per motivi vedi Perché non puoi eseguire operazioni bit per bit sul puntatore in C, e c'è un modo per aggirare questo?
Pertanto, al fine di eseguire operazioni bit per bit sui puntatori, è necessario eseguire il cast di puntatori per digitare unitpr_t e quindi eseguire operazioni bit per bit.
Ecco un esempio di una funzione che ho appena scritto per eseguire l'esclusiva bit per bit o di 2 puntatori da memorizzare in un elenco collegato XOR in modo che possiamo attraversare in entrambe le direzioni come un elenco doppiamente collegato ma senza la penalità di memorizzare 2 puntatori in ciascun nodo .
template <typename T>
T* xor_ptrs(T* t1, T* t2)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
}
Rischiando di ottenere un altro badge Necromancer, vorrei aggiungere un ottimo uso per uintptr_t (o anche intptr_t) e che sta scrivendo codice incorporato testabile. Scrivo principalmente codice incorporato indirizzato a vari processori arm e attualmente tensilica. Questi hanno varie larghezze di bus native e la tensilica è in realtà un'architettura di Harvard con bus di codice e dati separati che possono avere larghezze diverse. Uso uno stile di sviluppo basato su test per gran parte del mio codice, il che significa che eseguo test unitari per tutte le unità di codice che scrivo. Il test di unità su hardware di destinazione reale è una seccatura, quindi in genere scrivo tutto su un PC basato su Intel in Windows o Linux utilizzando Ceedling e GCC. Detto questo, un sacco di codice incorporato comporta manipolazione e indirizzamento dei bit. La maggior parte delle mie macchine Intel sono a 64 bit. Quindi, se hai intenzione di testare il codice di manipolazione degli indirizzi, hai bisogno di un oggetto generalizzato per fare matematica. Pertanto uintptr_t offre un modo indipendente dal computer per eseguire il debug del codice prima di provare a distribuire sull'hardware di destinazione. Un altro problema è per alcune macchine o persino modelli di memoria su alcuni compilatori, i puntatori di funzione e i puntatori di dati hanno larghezze diverse. Su quelle macchine il compilatore potrebbe non consentire nemmeno il casting tra le due classi, ma uintptr_t dovrebbe essere in grado di contenere entrambe.