Che cos'è il tipo di dati uintptr_t


Risposte:


199

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 :)


53
Nota che size_tdeve 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*)
MSalters,

3
per rappresentare la differenza tra due puntatori, hai ptrdiff_t. uintptr_tnon è pensato per questo.
jalf

3
@jalf: per differenza, sì, ma per la distanza , vorresti un tipo senza segno.
disegnato Dormann il

La definizione di uintptr_t non è apparentemente obbligatoria (quindi non standard) nemmeno in C ++ 11! cplusplus.com/reference/cstdint (ho ricevuto il suggerimento dalla risposta di Steve Jessop)
Antonio

2
@MarcusJ di unsigned intsolito non è abbastanza grande. Ma potrebbe essere abbastanza grande. Questo tipo esiste specificamente per rimuovere tutto "assumendo" .
Drew Dormann,

239

La prima cosa, al momento della domanda, uintptr_tnon 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_tpotrebbe 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_tche soddisfi i requisiti. Non so perché un'implementazione lo farebbe, ma lo standard lo consente.


8
Grazie per "<stdint.h>". La mia applicazione non è stata compilata a causa della dichiarazione uintptr_t. Ma quando leggo il tuo commento aggiungo "#include <stdint.h>" e sì ora funziona. Grazie!
JavaRunner,

2
Per allocare memoria allineata , ad esempio, tra gli altri usi?
legends2k

4
Un altro caso d'uso comune di lanciare un puntatore a un int è quello di creare un handle opaco che nasconda il puntatore. Ciò è utile per restituire un riferimento a un oggetto dalle API in cui si desidera mantenere l'oggetto privato nella libreria e impedire alle applicazioni di accedervi. L'applicazione è quindi costretta a utilizzare l'API per eseguire qualsiasi operazione sull'oggetto
Joel Cunningham,

3
@JoelCunningham: funziona ma non è molto diverso dall'uso 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.
Steve Jessop,

2
@CiroSantilli 烏坎 事件 2016 六四 事件 法轮功 Un caso d'uso comune è quello di passare solo un int a un'API che si aspetta un vuoto * a dati generici. Ti salva i tasti 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.
Francesco Dondi,

19

È 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_te lo manipoli come un normale numero intero, quindi esegui nuovamente il cast.


6
Certo che potresti farlo, ma ovviamente sarebbe un comportamento indefinito. Credo quindi che l'unica cosa che puoi fare con il risultato di un cast su uintptr_t sia passarlo su invariato e ricacciarlo - tutto il resto è UB.
sleske,

Ci sono momenti in cui è necessario giocare con i bit e normalmente genererebbe errori di compilazione. Un esempio comune è l'applicazione della memoria allineata a 16 byte per alcune applicazioni video e con prestazioni critiche. stackoverflow.com/questions/227897/…
Cloud

3
@sleske non è vero. Su macchine con tipi autoallineanti i due bit meno significativi di un puntatore saranno zero (perché gli indirizzi sono multipli di 4 o 8). Ho visto programmi che sfruttano questo per comprimere i dati ..
saadtaame

3
@saadtaame: Ho appena fatto notare che questo è UB secondo lo standard C . Ciò non significa che non possa essere definito su alcuni sistemi: compilatori e runtime sono liberi di definire un comportamento specifico per qualcosa che è UB nello standard C. Quindi non c'è contraddizione :-).
sleske,

2
Non è necessariamente esattamente la dimensione di un puntatore. Tutte le garanzie standard sono che la conversione di un void*valore del puntatore in uintptr_te viceversa produce un void*valore che è uguale al puntatore originale. uintptr_tdi 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.
Keith Thompson,

15

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));
  }

1
diverso dall'aritmetica dei bit è anche bello se si desidera avere una semantica basata sugli indirizzi anziché sul conteggio degli oggetti.
Alex,

4

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.


Non sono sicuro se rilevante ... Hai provato "typedef opachi"? Vide: youtube.com/watch?v=jLdSjh8oqmE
shycha

@sleske Vorrei che fosse disponibile in C. Ma avere stdint.h è meglio di niente. (Vorrei anche enumerazioni in cui si scrivesse più forte, ma la maggior parte dei debugger fa un buon lavoro per
capirle
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.