Ich bin wenig mit der Anwendbarkeit von verwechselt reinterpret_cast
vs static_cast
. Nach dem, was ich gelesen habe, besteht die allgemeine Regel darin, statische Umwandlung zu verwenden, wenn die Typen zur Kompilierungszeit interpretiert werden können, daher das Wort static
. Dies ist die Besetzung, die der C ++ - Compiler intern auch für implizite Besetzungen verwendet.
reinterpret_cast
s sind in zwei Szenarien anwendbar:
- Ganzzahlige Typen in Zeigertypen konvertieren und umgekehrt
- Konvertieren Sie einen Zeigertyp in einen anderen. Die allgemeine Idee, die ich bekomme, ist, dass dies nicht portierbar ist und vermieden werden sollte.
Wo ich ein wenig verwirrt bin, ist eine Verwendung, die ich brauche, ich rufe C ++ von C auf und der C-Code muss das C ++ - Objekt festhalten, damit es im Grunde genommen a enthält void*
. Welche Besetzung sollte verwendet werden, um zwischen dem void *
und dem Klassentyp zu konvertieren ?
Ich habe die Verwendung von beiden gesehen static_cast
und reinterpret_cast
? Obwohl nach dem, was ich gelesen habe, es static
besser erscheint , da die Besetzung zur Kompilierungszeit erfolgen kann? Obwohl es heißt reinterpret_cast
, von einem Zeigertyp in einen anderen zu konvertieren?
reinterpret_cast
passiert nicht zur Laufzeit. Sie sind beide Anweisungen zur Kompilierungszeit. Aus en.cppreference.com/w/cpp/language/reinterpret_cast : "Im Gegensatz zu static_cast, jedoch wie const_cast, wird der Ausdruck reinterpret_cast nicht in CPU-Anweisungen kompiliert. Es handelt sich lediglich um eine Compiler-Direktive, die den Compiler anweist, die Bitfolge zu behandeln (Objektdarstellung) des Ausdrucks, als hätte er den Typ new_type. "Antworten:
Der C ++ - Standard garantiert Folgendes:
static_cast
einen Zeiger auf und vonvoid*
bleibt die Adresse erhalten. Das heißt, im Folgendena
,b
undc
alle auf die gleiche Adresse:reinterpret_cast
garantiert nur, dass Sie den ursprünglichen Wert erhalten , wenn Sie einen Zeiger auf einen anderen Typ und dannreinterpret_cast
auf den ursprünglichen Typ zurücksetzen. Also im Folgenden:a
undc
enthalten den gleichen Wert, aber der Wert vonb
ist nicht angegeben. (In der Praxis enthält es normalerweise dieselbe Adresse wiea
undc
, dies ist jedoch nicht im Standard angegeben und gilt möglicherweise nicht für Computer mit komplexeren Speichersystemen.)Zum Casting von und nach
void*
,static_cast
sollte bevorzugt werden.quelle
b
wird in C ++ 11 bei Verwendung nicht mehr angegebenreinterpret_cast
. Und in C ++ 03 war es verboten, eine Besetzung vonint*
to durchzuführen (obwohl Compiler dies nicht implementierten und es unpraktisch war, wurde es daher für C ++ 11 geändert).void*
reinterpret_cast
Ein Fall, wenn dies
reinterpret_cast
erforderlich ist, ist die Schnittstelle zu undurchsichtigen Datentypen. Dies tritt häufig in Hersteller-APIs auf, über die der Programmierer keine Kontrolle hat. Hier ist ein erfundenes Beispiel, in dem ein Anbieter eine API zum Speichern und Abrufen beliebiger globaler Daten bereitstellt:Um diese API verwenden zu können, muss der Programmierer seine Daten hin
VendorGlobalUserData
und zurück übertragen.static_cast
wird nicht funktionieren, muss man verwendenreinterpret_cast
:Im Folgenden finden Sie eine erfundene Implementierung der Beispiel-API:
quelle
void*
?USpoofChecker*
, bei denenUSpoofChecker
es sich um eine leere Struktur handelt. Unter der Haube wird es jedoch immer dann, wenn Sie a übergebenUSpoofChecker*
,reinterpret_cast
einem internen C ++ - Typ unterzogen .Die kurze Antwort: Wenn Sie nicht wissen, wofür es
reinterpret_cast
steht, verwenden Sie es nicht. Wenn Sie es in Zukunft brauchen, werden Sie es wissen.Vollständige Antwort:
Betrachten wir grundlegende Zahlentypen.
Wenn Sie zum Beispiel
int(12)
inunsigned float (12.0f)
Ihren Prozessor konvertieren , müssen Sie einige Berechnungen aufrufen, da beide Zahlen unterschiedliche Bitdarstellungen haben. Dafürstatic_cast
steht.Wenn Sie dagegen
reinterpret_cast
die CPU aufrufen, werden keine Berechnungen aufgerufen . Es behandelt nur eine Reihe von Bits im Speicher, als hätte es einen anderen Typ. Also , wenn Sie konvertieren ,int*
umfloat*
mit diesem Begriff, hat der neuen Wert (nach Zeigern dereferecing) nichts mit dem alten Wert im mathematischen Sinne zu tun.Beispiel: Es ist wahr, dass
reinterpret_cast
es aus einem Grund nicht portierbar ist - Bytereihenfolge (Endianness). Dies ist jedoch oft überraschend der beste Grund, es zu verwenden. Stellen wir uns das Beispiel vor: Sie müssen die binäre 32-Bit-Zahl aus der Datei lesen und wissen, dass es sich um Big Endian handelt. Ihr Code muss generisch sein und funktioniert ordnungsgemäß auf Big-Endian-Systemen (z. B. ARM) und Little-Endian-Systemen (z. B. x86). Sie müssen also die Bytereihenfolge überprüfen.Es ist zur Kompilierungszeit bekannt, sodass SieSie können eine Funktion schreiben, um dies zu erreichen:constexpr
Funktionen schreiben können:Erläuterung: Die binäre Darstellung
x
im Speicher kann0000'0000'0000'0001
(groß) oder0000'0001'0000'0000
(Little Endian) sein. Nach dem Neuinterpretieren des Castings könnte das Byte unter demp
Zeiger jeweils0000'0000
oder sein0000'0001
. Wenn Sie statisches Gießen verwenden, wird es immer sein0000'0001
, egal welche Endianness verwendet wird.BEARBEITEN:
In der ersten Version habe ich Beispielfunktion gemacht
is_little_endian
zu seinconstexpr
. Es kompiliert gut auf dem neuesten gcc (8.3.0), aber der Standard sagt, dass es illegal ist. Der Clang-Compiler weigert sich, es zu kompilieren (was korrekt ist).quelle
short
16 Bit im Speicher benötigt werden. Korrigiert.Die Bedeutung von
reinterpret_cast
wird nicht durch den C ++ - Standard definiert. Theoretischreinterpret_cast
könnte daher Ihr Programm abstürzen. In der Praxis versuchen Compiler, das zu tun, was Sie erwarten, nämlich die Teile Ihrer Übergabe so zu interpretieren, als wären sie der Typ, auf den Sie übertragen. Wenn Sie wissen, was die Compiler, die Sie verwenden werden, damit machenreinterpret_cast
, können Sie es verwenden, aber zu sagen, dass es portabel ist, würde lügen.Für den Fall, den Sie beschreiben, und so ziemlich jeden Fall, den Sie in Betracht ziehen
reinterpret_cast
, können Siestatic_cast
stattdessen eine andere Alternative verwenden. Der Standard hat unter anderemstatic_cast
Folgendes zu sagen, was Sie erwarten können (§5.2.9):Für Ihren Anwendungsfall scheint es also ziemlich klar zu sein, dass das Standardisierungskomitee für Sie vorgesehen ist
static_cast
.quelle
reinterpret_crash
. Ein Compiler-Fehler hindert mich auf keinen Fall daran, mein Neuinterpretationsprogramm zum Absturz zu bringen. Ich werde einen Fehler so schnell wie möglich melden! </template<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; }
Eine Verwendung von reinterpret_cast ist, wenn Sie bitweise Operationen auf (IEEE 754) Floats anwenden möchten. Ein Beispiel hierfür war der Fast Inverse Square-Root-Trick:
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
Es behandelt die binäre Darstellung des Floats als Ganzzahl, verschiebt sie nach rechts und subtrahiert sie von einer Konstanten, wodurch der Exponent halbiert und negiert wird. Nach der Rückkonvertierung in einen Float wird eine Newton-Raphson-Iteration durchgeführt, um diese Annäherung genauer zu machen:
Dies wurde ursprünglich in C geschrieben, verwendet also C-Casts, aber die analoge C ++ - Cast ist der reinterpret_cast.
quelle
error: invalid cast of an rvalue expression of type 'int64_t {aka long long int}' to type 'double&' reinterpret_cast<double&>((reinterpret_cast<int64_t&>(d) >> 1) + (1L << 61))
- ideone.com/6S4ijcreinterpret_cast
durch ersetzememcpy
, ist es immer noch UB?memcpy
würde es definitiv legal machen.Sie können reinterprete_cast verwenden, um die Vererbung beim Kompilieren zu überprüfen.
Schauen Sie hier: Verwenden Sie reinterpret_cast, um die Vererbung beim Kompilieren zu überprüfen
quelle
Ich versuchte zu schließen und schrieb eine einfache sichere Besetzung unter Verwendung von Vorlagen. Beachten Sie, dass diese Lösung nicht garantiert, Zeiger auf Funktionen zu setzen.
quelle
reinterpret_cast
bereits in dieser Situation: "Ein Objektzeiger kann explizit in einen Objektzeiger eines anderen Typs konvertiert werden. [72] Wenn ein Wertv
des Objektzeigertyps in den Objektzeigertyp" Zeiger auf LebenslaufT
" konvertiert wird , das Ergebnis iststatic_cast<cv T*>(static_cast<cv void*>(v))
. " - N3797.c++2003
Standard kann ich nicht feststellen , dassreinterpret_cast
tutstatic_cast<cv T*>(static_cast<cv void*>(v))
C++03
es warC++98
. Unzählige Projekte verwendeten altes C ++ anstelle von portablem C. Manchmal muss man sich um Portabilität kümmern. Beispielsweise müssen Sie denselben Code unter Solaris, AIX, HPUX und Windows unterstützen. Wenn es um Compilerabhängigkeit und Portabilität geht, ist es schwierig. Ein gutes Beispiel für die Einführung einer Portabilitätshölle ist die Verwendung einesreinterpret_cast
in Ihrem CodeZuerst haben Sie einige Daten in einem bestimmten Typ wie int hier:
Dann möchten Sie auf dieselbe Variable wie auf einen anderen Typ wie float zugreifen: Sie können zwischen diesen entscheiden
oder
KURZDARSTELLUNG: Dies bedeutet, dass derselbe Speicher als anderer Typ verwendet wird. Sie können also binäre Darstellungen von Floats als int-Typ wie oben in Floats konvertieren. 0x80000000 ist zum Beispiel -0 (die Mantisse und der Exponent sind null, aber das Vorzeichen, die msb, ist eins. Dies funktioniert auch für Doppelte und lange Doppelte.
OPTIMIEREN: Ich denke, reinterpret_cast würde in vielen Compilern optimiert, während das c-Casting durch Zeigerarithmetik erfolgt (der Wert muss in den Speicher kopiert werden, da Zeiger nicht auf CPU-Register zeigen können).
HINWEIS: In beiden Fällen sollten Sie den Cast-Wert vor dem Cast in einer Variablen speichern! Dieses Makro könnte helfen:
quelle
reinterpret_cast
Formint
zufloat&
undefiniert Verhalten.Ein Grund für die Verwendung
reinterpret_cast
ist, wenn eine Basisklasse keine vtable hat, eine abgeleitete Klasse jedoch. In diesem Fall,static_cast
undreinterpret_cast
wird in verschiedenen Zeigerwerten führen (dies ist der Fall , atypische von erwähnten wäre JALF oben ). Nur als Haftungsausschluss sage ich nicht, dass dies Teil des Standards ist, sondern die Implementierung mehrerer weit verbreiteter Compiler.Nehmen Sie als Beispiel den folgenden Code:
Welche Ausgaben so etwas wie:
In allen Compilern, die ich ausprobiert habe (MSVC 2015 & 2017, Clang 8.0.0, gcc 9.2, ICC 19.0.1 - siehe Godbolt für die letzten 3 ), unterscheidet sich das Ergebnis
static_cast
von demreinterpret_cast
von 2 (4 für MSVC). Der einzige Compiler, der vor dem Unterschied warnte, war clang mit:Eine letzte Einschränkung ist, dass wenn die Basisklasse keine Datenelemente (z. B. die
int i;
) hat, clang, gcc und icc dieselbe Adressereinterpret_cast
wie für zurückgebenstatic_cast
, während MSVC dies immer noch nicht tut.quelle
Hier ist eine Variante von Avi Ginsburgs Programm, die die
reinterpret_cast
von Chris Luengo, flodin und cmdLP erwähnte Eigenschaft deutlich macht: Der Compiler behandelt den Speicherort, auf den verwiesen wird, als wäre er ein Objekt des neuen Typs:Was zu einer Ausgabe wie dieser führt:
Es ist ersichtlich, dass das B-Objekt zuerst als B-spezifische Daten im Speicher erstellt wird, gefolgt vom eingebetteten A-Objekt. Das
static_cast
gibt die Adresse des eingebetteten A-Objekts korrekt zurück, und der durchstatic_cast
korrekt erstellte Zeiger gibt den Wert des Datenfelds an. Der durchreinterpret_cast
Leckereien erzeugte Zeigerb
den Speicherort des ihn wie ein einfaches A-Objekt. Wenn der Zeiger versucht, das Datenfeld abzurufen, gibt er einige B-spezifische Daten zurück, als wäre es der Inhalt dieses Feldes.Eine Verwendung von
reinterpret_cast
besteht darin, einen Zeiger in eine vorzeichenlose Ganzzahl umzuwandeln (wenn Zeiger und vorzeichenlose Ganzzahlen dieselbe Größe haben):int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
quelle
Schnelle Antwort: Verwenden
static_cast
Sie, wenn es kompiliert wird, andernfalls greifen Sie auf zurückreinterpret_cast
.quelle
Lesen Sie die FAQ ! Das Speichern von C ++ - Daten in C kann riskant sein.
In C ++ kann ein Zeiger auf ein Objekt ohne Umwandlungen konvertiert
void *
werden. Aber umgekehrt ist es nicht wahr. Sie benötigen einestatic_cast
, um den ursprünglichen Zeiger zurückzubekommen.quelle