Wann soll reinterpret_cast verwendet werden?

459

Ich bin wenig mit der Anwendbarkeit von verwechselt reinterpret_castvs 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_casts 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_castund reinterpret_cast? Obwohl nach dem, was ich gelesen habe, es staticbesser erscheint , da die Besetzung zur Kompilierungszeit erfolgen kann? Obwohl es heißt reinterpret_cast, von einem Zeigertyp in einen anderen zu konvertieren?

HeretoLearn
quelle
9
reinterpret_castpassiert 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. "
Cris Luengo
@HeretoLearn, ist es möglich, die relevanten Codeteile aus den Dateien * .c und * .cpp hinzuzufügen? Ich denke, es kann die Darstellung der Frage verbessern.
OrenIshShalom

Antworten:

442

Der C ++ - Standard garantiert Folgendes:

static_casteinen Zeiger auf und von void* bleibt die Adresse erhalten. Das heißt, im Folgenden a, bund calle auf die gleiche Adresse:

int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);

reinterpret_castgarantiert nur, dass Sie den ursprünglichen Wert erhalten , wenn Sie einen Zeiger auf einen anderen Typ und dann reinterpret_castauf den ursprünglichen Typ zurücksetzen. Also im Folgenden:

int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);

aund centhalten den gleichen Wert, aber der Wert von bist nicht angegeben. (In der Praxis enthält es normalerweise dieselbe Adresse wie aund c, 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.

jalf
quelle
18
Ich mag die Tatsache, dass 'b' undefiniert ist. Es hält dich davon ab, dumme Dinge damit zu machen. Wenn Sie etwas in einen anderen Zeigertyp umwandeln, fragen Sie nach Problemen, und die Tatsache, dass Sie sich nicht darauf verlassen können, macht Sie vorsichtiger. Wenn Sie static_cast <> oben verwendet haben, welchen Nutzen hat das 'b' überhaupt?
Martin York
3
Ich dachte, dass reinterpret_cast <> das gleiche Bitmuster garantiert. (Dies ist nicht dasselbe wie ein gültiger Zeiger auf einen anderen Typ).
Martin York
37
Der Wert von bwird in C ++ 11 bei Verwendung nicht mehr angegeben reinterpret_cast. Und in C ++ 03 war es verboten, eine Besetzung von int*to durchzuführen (obwohl Compiler dies nicht implementierten und es unpraktisch war, wurde es daher für C ++ 11 geändert). void*reinterpret_cast
Johannes Schaub - Litb
55
Dies beantwortet nicht die Frage, wann reinterpret_cast verwendet werden soll.
Einpoklum
6
@LokiAstari Ich denke, nicht spezifiziert hält dich nicht davon ab, dumme Dinge zu tun. Es hält Sie nur auf, wenn Sie sich daran erinnern, dass es nicht spezifiziert ist. Großer Unterschied. Persönlich mag ich nicht spezifiziert. Zu viel, um sich zu erinnern.
Helin Wang
158

Ein Fall, wenn dies reinterpret_casterforderlich 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:

// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();

Um diese API verwenden zu können, muss der Programmierer seine Daten hin VendorGlobalUserDataund zurück übertragen. static_castwird nicht funktionieren, muss man verwenden reinterpret_cast:

// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;

struct MyUserData {
    MyUserData() : m(42) {}
    int m;
};

int main() {
    MyUserData u;

        // store global data
    VendorGlobalUserData d1;
//  d1 = &u;                                          // compile error
//  d1 = static_cast<VendorGlobalUserData>(&u);       // compile error
    d1 = reinterpret_cast<VendorGlobalUserData>(&u);  // ok
    VendorSetUserData(d1);

        // do other stuff...

        // retrieve global data
    VendorGlobalUserData d2 = VendorGetUserData();
    MyUserData * p = 0;
//  p = d2;                                           // compile error
//  p = static_cast<MyUserData *>(d2);                // compile error
    p = reinterpret_cast<MyUserData *>(d2);           // ok

    if (p) { cout << p->m << endl; }
    return 0;
}

Im Folgenden finden Sie eine erfundene Implementierung der Beispiel-API:

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
jwfearn
quelle
7
Ja, das ist ungefähr die einzige sinnvolle Verwendung von reinterpret_cast, die mir einfällt.
Jalf
8
Dies mag eine späte Frage sein, aber warum wird die Hersteller-API dafür nicht verwendet void*?
Xeo
19
@Xeo Sie verwenden void * nicht, weil sie dann beim Kompilieren (einige) Typprüfungen verlieren.
Jesus
4
Ein praktischer Anwendungsfall für "undurchsichtige" Datentypen ist, wenn Sie eine API für C verfügbar machen möchten, aber die Implementierung in C ++ schreiben möchten. Die Intensivstation ist ein Beispiel für eine Bibliothek, die dies an mehreren Stellen tut. In der Spoof-Checker-API werden beispielsweise Zeiger vom Typ behandelt USpoofChecker*, bei denen USpoofCheckeres sich um eine leere Struktur handelt. Unter der Haube wird es jedoch immer dann, wenn Sie a übergeben USpoofChecker*, reinterpret_casteinem internen C ++ - Typ unterzogen .
sffc
@sffc Warum nicht den C-Strukturtyp für den Benutzer verfügbar machen?
Gupta
101

Die kurze Antwort: Wenn Sie nicht wissen, wofür es reinterpret_caststeht, 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)in unsigned float (12.0f)Ihren Prozessor konvertieren , müssen Sie einige Berechnungen aufrufen, da beide Zahlen unterschiedliche Bitdarstellungen haben. Dafür static_caststeht.

Wenn Sie dagegen reinterpret_castdie 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*um float*mit diesem Begriff, hat der neuen Wert (nach Zeigern dereferecing) nichts mit dem alten Wert im mathematischen Sinne zu tun.

Beispiel: Es ist wahr, dassreinterpret_castes 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 Sie constexprFunktionen schreiben können: Sie können eine Funktion schreiben, um dies zu erreichen:

/*constexpr*/ bool is_little_endian() {
  std::uint16_t x=0x0001;
  auto p = reinterpret_cast<std::uint8_t*>(&x);
  return *p != 0;
}

Erläuterung: Die binäre Darstellungxim Speicher kann0000'0000'0000'0001(groß) oder0000'0001'0000'0000(Little Endian) sein. Nach dem Neuinterpretieren des Castings könnte das Byte unter dempZeiger jeweils0000'0000oder 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 sein constexpr. 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).

Jaskmar
quelle
1
Schönes Beispiel! Ich würde kurz für uint16_t und vorzeichenloses Zeichen für uint8_t ersetzen, um es für Menschen weniger dunkel zu machen.
Jan Turoň
@ JanTuroň wahr, wir können nicht davon ausgehen, dass short16 Bit im Speicher benötigt werden. Korrigiert.
Jaskmar
1
Das Beispiel ist falsch. reinterpret_cast ist in constexpr-Funktionen nicht erlaubt
Michael Veksler
1
Zunächst wird dieser Code sowohl von der neuesten Version (7.0.0) als auch von gcc (8.2.0) abgelehnt. Leider habe ich die Einschränkung in der formalen Sprache nicht gefunden. Alles was ich finden konnte war social.msdn.microsoft.com/Forums/vstudio/en-US/…
Michael Veksler
2
Insbesondere wird in en.cppreference.com/w/cpp/language/constant_expression (Punkt 16) klar angegeben, dass reinterpret_cast nicht in einem konstanten Ausdruck verwendet werden kann. Schauen Sie sich auch die Seiten 125-126 von github.com/cplusplus/draft/blob/master/papers/N3797.pdf (5.19 konstante Ausdrücke) an, die eine Neuinterpretation von Sendungen ausdrücklich ausschließen. Dann 7.1.5 Der constexpr-Spezifizierer Punkt 5 (Seite 146) * Für eine nicht vorlagenbehaftete, nicht standardmäßige constexpr-Funktion ... wenn keine Argumentwerte vorhanden sind, so dass ... ein ausgewerteter Unterausdruck eines Kernkonstantenausdrucks sein könnte (5.19 ), das Programm ist schlecht geformt *
Michael Veksler
20

Die Bedeutung von reinterpret_castwird nicht durch den C ++ - Standard definiert. Theoretisch reinterpret_castkö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 machen reinterpret_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 Sie static_caststattdessen eine andere Alternative verwenden. Der Standard hat unter anderem static_castFolgendes zu sagen, was Sie erwarten können (§5.2.9):

Ein Wert vom Typ "Zeiger auf Lebenslauf ungültig" kann explizit in einen Zeiger auf den Objekttyp konvertiert werden. Ein Wert vom Typ Zeiger auf Objekt, der in „Zeiger auf Lebenslauf ungültig“ und zurück zum ursprünglichen Zeigertyp konvertiert wurde, hat seinen ursprünglichen Wert.

Für Ihren Anwendungsfall scheint es also ziemlich klar zu sein, dass das Standardisierungskomitee für Sie vorgesehen ist static_cast.

Flodin
quelle
5
Ihr Programm nicht ganz zum Absturz bringen. Der Standard bietet einige Garantien für reinterpret_cast. Nur nicht so viele, wie die Leute oft erwarten.
Jalf
1
Nicht, wenn Sie es richtig verwenden. Das heißt, reinterpret_cast von A nach B nach A ist absolut sicher und genau definiert. Aber der Wert von B ist nicht spezifiziert, und ja, wenn Sie sich darauf verlassen, können schlimme Dinge passieren. Die Besetzung selbst ist jedoch sicher genug, solange Sie sie nur so verwenden, wie es der Standard zulässt. ;)
Jalf
55
lol, ich vermute, dass reinterpret_crash tatsächlich Ihr Programm zum Absturz bringen könnte. Aber reinterpret_cast wird nicht. ;)
Jalf
5
<irony> Ich habe es auf meinem Compiler versucht und irgendwie hat es sich geweigert, es zu kompilieren 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! </
Iron
18
@ Paercebaltemplate<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; }
12

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:

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the deuce? 
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

Dies wurde ursprünglich in C geschrieben, verwendet also C-Casts, aber die analoge C ++ - Cast ist der reinterpret_cast.

Adam P. Goucher
quelle
1
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/6S4ijc
Orwellophile
1
Der Standard besagt, dass dies ein undefiniertes Verhalten ist: en.cppreference.com/w/cpp/language/reinterpret_cast (unter "Typ-Aliasing")
Cris Luengo
@CrisLuengo Wenn ich alle reinterpret_castdurch ersetze memcpy, ist es immer noch UB?
Sanddorn
@sandthorn: Dies ist UB gemäß dem Standard, aber wenn es für Ihre Architektur funktioniert, machen Sie sich darüber keine Sorgen. Ich nehme an, dieser Trick ist für jeden Compiler für Intel-Architekturen in Ordnung. Es könnte auf anderen Architekturen nicht wie beabsichtigt funktionieren (oder sogar abstürzen) - zum Beispiel könnte es möglich sein, dass Floats und Longs in separaten Speicherfächern gespeichert werden (nicht, dass ich eine solche Architektur kenne, es ist nur ein Argument ...) . memcpywürde es definitiv legal machen.
Cris Luengo
2
template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

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.

Sasha Zezulinsky
quelle
1
Was? Warum die Mühe? Genau dies geschieht reinterpret_castbereits in dieser Situation: "Ein Objektzeiger kann explizit in einen Objektzeiger eines anderen Typs konvertiert werden. [72] Wenn ein Wert v des Objektzeigertyps in den Objektzeigertyp" Zeiger auf Lebenslauf T " konvertiert wird , das Ergebnis ist static_cast<cv T*>(static_cast<cv void*>(v)). " - N3797.
underscore_d
Wie bei c++2003Standard kann ich nicht feststellen , dass reinterpret_casttutstatic_cast<cv T*>(static_cast<cv void*>(v))
Sasha Zezulinsky
1
OK, stimmt, aber mir ist eine Version von vor 13 Jahren egal, und die meisten Programmierer sollten es auch nicht tun, wenn sie (wie wahrscheinlich) dies vermeiden können. Antworten und Kommentare sollten wirklich den neuesten verfügbaren Standard widerspiegeln, sofern nicht anders angegeben ... IMHO. Wie auch immer, ich denke, das Komitee hatte das Bedürfnis, dies nach 2003 explizit hinzuzufügen (weil IIRC in C ++ 11 dasselbe war)
underscore_d
Bevor C++03es war C++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 eines reinterpret_castin Ihrem Code
Sasha Zezulinsky
Wenn Sie sich wie ich gerne nur auf Plattformen beschränken, die mit der neuesten und besten Version der Sprache gut funktionieren, ist Ihr Einwand ein strittiger Punkt.
underscore_d
1

Zuerst haben Sie einige Daten in einem bestimmten Typ wie int hier:

int x = 0x7fffffff://==nan in binary representation

Dann möchten Sie auf dieselbe Variable wie auf einen anderen Typ wie float zugreifen: Sie können zwischen diesen entscheiden

float y = reinterpret_cast<float&>(x);

//this could only be used in cpp, looks like a function with template-parameters

oder

float y = *(float*)&(x);

//this could be used in c and cpp

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:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })
cmdLP
quelle
Es ist wahr, dass "es bedeutet, dass derselbe Speicher als ein anderer Typ verwendet wird", aber es ist auf ein bestimmtes Paar von Typen beschränkt. In Ihrem Beispiel reinterpret_castForm intzu float&undefiniert Verhalten.
Jaskmar
1

Ein Grund für die Verwendung reinterpret_castist, wenn eine Basisklasse keine vtable hat, eine abgeleitete Klasse jedoch. In diesem Fall, static_castund reinterpret_castwird 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:

#include <cstdio>

class A {
public:
    int i;
};

class B : public A {
public:
    virtual void func() {  }
};

int main()
{
    B b;
    const A* a = static_cast<A*>(&b);
    const A* ar = reinterpret_cast<A*>(&b);

    printf("&b = %p\n", &b);
    printf(" a = %p\n", a);
    printf("ar = %p\n", ar);
    printf("difference = %ld\n", (long int)(a - ar));

    return 0;
}

Welche Ausgaben so etwas wie:

& b = 0x7ffe10e68b38
a = 0x7ffe10e68b40
ar = 0x7ffe10e68b38
Differenz = 2

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_castvon dem reinterpret_castvon 2 (4 für MSVC). Der einzige Compiler, der vor dem Unterschied warnte, war clang mit:

17:16: Warnung: 'reinterpret_cast' von Klasse 'B *' zu seiner Basis bei einem Offset ungleich Null 'A *' verhält sich anders als 'static_cast' [-Wreinterpret-Basisklasse]
const A * ar = reinterpret_cast (& b) ;;
^ ~~~~~~~~~~~~~~~~~~~~~~~
17:16: Hinweis: Verwenden Sie 'static_cast', um den Zeiger beim Upcasting von
const A * ar = reinterpret_cast (& b) korrekt anzupassen. ;;
^
~~~~~~~~~~~~~~~~ static_cast

Eine letzte Einschränkung ist, dass wenn die Basisklasse keine Datenelemente (z. B. die int i;) hat, clang, gcc und icc dieselbe Adresse reinterpret_castwie für zurückgeben static_cast, während MSVC dies immer noch nicht tut.

Avi Ginsburg
quelle
1

Hier ist eine Variante von Avi Ginsburgs Programm, die die reinterpret_castvon 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:

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

class A
{
public:
    int i;
};

class B : public A
{
public:
    virtual void f() {}
};

int main()
{
    string s;
    B b;
    b.i = 0;
    A* as = static_cast<A*>(&b);
    A* ar = reinterpret_cast<A*>(&b);
    B* c = reinterpret_cast<B*>(ar);

    cout << "as->i = " << hex << setfill('0')  << as->i << "\n";
    cout << "ar->i = " << ar->i << "\n";
    cout << "b.i   = " << b.i << "\n";
    cout << "c->i  = " << c->i << "\n";
    cout << "\n";
    cout << "&(as->i) = " << &(as->i) << "\n";
    cout << "&(ar->i) = " << &(ar->i) << "\n";
    cout << "&(b.i) = " << &(b.i) << "\n";
    cout << "&(c->i) = " << &(c->i) << "\n";
    cout << "\n";
    cout << "&b = " << &b << "\n";
    cout << "as = " << as << "\n";
    cout << "ar = " << ar << "\n";
    cout << "c  = " << c  << "\n";

    cout << "Press ENTER to exit.\n";
    getline(cin,s);
}

Was zu einer Ausgabe wie dieser führt:

as->i = 0
ar->i = 50ee64
b.i   = 0
c->i  = 0

&(as->i) = 00EFF978
&(ar->i) = 00EFF974
&(b.i) = 00EFF978
&(c->i) = 00EFF978

&b = 00EFF974
as = 00EFF978
ar = 00EFF974
c  = 00EFF974
Press ENTER to exit.

Es ist ersichtlich, dass das B-Objekt zuerst als B-spezifische Daten im Speicher erstellt wird, gefolgt vom eingebetteten A-Objekt. Das static_castgibt die Adresse des eingebetteten A-Objekts korrekt zurück, und der durch static_castkorrekt erstellte Zeiger gibt den Wert des Datenfelds an. Der durch reinterpret_castLeckereien 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_castbesteht 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);

TRPh
quelle
-6

Schnelle Antwort: Verwenden static_castSie, wenn es kompiliert wird, andernfalls greifen Sie auf zurück reinterpret_cast.

Marius K.
quelle
-16

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 eine static_cast, um den ursprünglichen Zeiger zurückzubekommen.

dirkgently
quelle