Was ist uintptr_t
und wofür kann es verwendet werden?
std::uintptr_t
und std::intptr_t
ein optionales C ++ 11.
Was ist uintptr_t
und wofür kann es verwendet werden?
std::uintptr_t
und std::intptr_t
ein optionales C ++ 11.
Antworten:
uintptr_t
ist ein vorzeichenloser Integer-Typ, der einen Datenzeiger speichern kann. Dies bedeutet normalerweise, dass es die gleiche Größe wie ein Zeiger hat.
Es ist optional in C ++ 11 und späteren Standards definiert.
Ein häufiger Grund für den Wunsch nach einem Integer-Typ, der den Zeigertyp einer Architektur enthalten kann, besteht darin, ganzzahlspezifische Operationen an einem Zeiger auszuführen oder den Typ eines Zeigers zu verdecken, indem er als ganzzahliges "Handle" bereitgestellt wird.
Bearbeiten: Beachten Sie, dass Steve Jessop einige sehr interessante zusätzliche Details (die ich nicht stehlen werde) in einer anderen Antwort hier für Sie pedantischen Typen hat :)
size_t
nur ausreichen muss, um die Größe des größten Objekts zu halten, und kleiner als ein Zeiger sein kann. Dies wäre auf segmentierten Architekturen wie dem 8086 (16 Bit size_t
, aber 32 Bit void*
) zu erwarten
ptrdiff_t
. uintptr_t
ist nicht dafür gedacht.
unsigned int
normalerweise nicht groß genug. Aber es könnte groß genug sein. Dieser Typ existiert speziell, um alle "Annahmen" zu entfernen .
Als die Frage gestellt wurde, uintptr_t
war das erste, was nicht in C ++ war. Es ist in C99, in <stdint.h>
, als optionaler Typ. Viele C ++ 03-Compiler stellen diese Datei zur Verfügung. Es ist auch in C ++ 11, <cstdint>
wo es wiederum optional ist und sich für die Definition auf C99 bezieht.
In C99 ist es definiert als "ein vorzeichenloser Integer-Typ mit der Eigenschaft, dass jeder gültige Zeiger auf void in diesen Typ konvertiert und dann wieder in einen Zeiger auf void konvertiert werden kann und das Ergebnis dem ursprünglichen Zeiger entspricht".
Nehmen Sie das als das, was es sagt. Es sagt nichts über die Größe aus.
uintptr_t
könnte die gleiche Größe haben wie a void*
. Es könnte größer sein. Es könnte möglicherweise kleiner sein, obwohl sich eine solche C ++ - Implementierung pervers nähert. Auf einer hypothetischen Plattform mit void*
32 Bit, aber nur 24 Bit virtuellem Adressraum können Sie beispielsweise ein 24-Bit-Format verwenden, uintptr_t
das die Anforderungen erfüllt. Ich weiß nicht, warum eine Implementierung das tun würde, aber der Standard erlaubt es.
void*
. Dies wirkt sich jedoch auf mögliche zukünftige Richtungen aus, insbesondere wenn Sie Änderungen vornehmen möchten, um etwas zu verwenden, das eigentlich nur ein ganzzahliges Handle und überhaupt kein konvertierter Zeiger ist.
typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;
. Stattdessen : callback.dataPtr = (void*)val
. Auf der anderen Seite bekommt man es natürlich void*
und muss es zurückwerfen int
.
Es ist ein vorzeichenloser Integer-Typ, der genau die Größe eines Zeigers hat. Wann immer Sie mit einem Zeiger etwas Ungewöhnliches tun müssen - wie zum Beispiel alle Bits invertieren (fragen Sie nicht warum), wandeln Sie uintptr_t
ihn in eine gewöhnliche Ganzzahl um und manipulieren ihn dann zurück.
void*
Zeigerwerts in uintptr_t
und wieder zurück einen void*
Wert ergibt , der dem ursprünglichen Zeiger entspricht. uintptr_t
hat normalerweise die gleiche Größe wie void*
, aber das ist weder garantiert, noch gibt es eine Garantie dafür, dass die Bits des konvertierten Werts eine bestimmte Bedeutung haben. Und es gibt keine Garantie dafür, dass ein konvertierter Zeiger-zu-Funktion-Wert ohne Informationsverlust gespeichert werden kann. Schließlich ist es nicht garantiert zu existieren.
Es gibt bereits viele gute Antworten auf den Teil "Was ist der Datentyp uintptr_t?". Ich werde versuchen, das "Wofür kann es verwendet werden?" Teil in diesem Beitrag.
Hauptsächlich für bitweise Operationen an Zeigern. Denken Sie daran, dass in C ++ keine bitweisen Operationen an Zeigern ausgeführt werden können. Aus Gründen siehe Warum können Sie in C keine bitweisen Operationen am Zeiger ausführen, und gibt es einen Weg, dies zu umgehen?
Um bitweise Operationen an Zeigern durchzuführen, müsste man also Zeiger umwandeln, um unitpr_t einzugeben, und dann bitweise Operationen ausführen.
Hier ist ein Beispiel für eine Funktion, die ich gerade geschrieben habe, um bitweise exklusiv zu arbeiten, oder für 2 Zeiger, die in einer verknüpften XOR-Liste gespeichert werden sollen, damit wir wie eine doppelt verknüpfte Liste in beide Richtungen gehen können, ohne die Strafe, 2 Zeiger in jedem Knoten zu speichern .
template <typename T>
T* xor_ptrs(T* t1, T* t2)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
}
Da ich das Risiko habe, ein weiteres Nekromanten-Abzeichen zu erhalten, möchte ich eine sehr gute Verwendung für uintptr_t (oder sogar intptr_t) hinzufügen, bei der testbarer eingebetteter Code geschrieben wird. Ich schreibe hauptsächlich eingebetteten Code für verschiedene Arm- und derzeit Tensilica-Prozessoren. Diese haben verschiedene native Busbreiten und die Tensilica ist tatsächlich eine Harvard-Architektur mit separaten Code- und Datenbussen, die unterschiedliche Breiten haben können. Ich verwende einen testgetriebenen Entwicklungsstil für einen Großteil meines Codes, was bedeutet, dass ich Unit-Tests für alle Code-Einheiten durchführe, die ich schreibe. Unit-Tests auf der tatsächlichen Zielhardware sind mühsam, daher schreibe ich normalerweise alles auf einem Intel-basierten PC, entweder unter Windows oder Linux, mit Ceedling und GCC. Abgesehen davon beinhaltet viel eingebetteter Code Bit-Twiddling und Adressmanipulationen. Die meisten meiner Intel-Maschinen sind 64-Bit. Wenn Sie also den Adressmanipulationscode testen möchten, benötigen Sie ein verallgemeinertes Objekt, mit dem Sie rechnen können. Mit uintptr_t können Sie Ihren Code daher unabhängig vom Computer debuggen, bevor Sie versuchen, ihn auf der Zielhardware bereitzustellen. Ein weiteres Problem besteht darin, dass für einige Maschinen oder sogar Speichermodelle auf einigen Compilern Funktionszeiger und Datenzeiger unterschiedliche Breiten haben. Auf diesen Computern erlaubt der Compiler möglicherweise nicht einmal das Umwandeln zwischen den beiden Klassen, aber uintptr_t sollte auch in der Lage sein, zu halten.