Namensräume
Varianten
Aktionen

reinterpret_cast-Konvertierung

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Konvertiert zwischen Typen durch Neudefinition des zugrunde liegenden Bitmusters.

Inhalt

[bearbeiten] Syntax

reinterpret_cast< Zieltyp >( Ausdruck )

Gibt einen Wert vom Typ Zieltyp zurück.

[bearbeiten] Erklärung

Im Gegensatz zu static_cast, aber wie const_cast, kompiliert der reinterpret_cast-Ausdruck nicht zu CPU-Instruktionen (außer bei der Konvertierung zwischen Ganzzahlen und Zeigern oder zwischen Zeigern auf obskuren Architekturen, bei denen die Zeigerdarstellung von ihrem Typ abhängt). Er ist hauptsächlich eine Direktive zur Kompilierungszeit, die den Compiler anweist, den Ausdruck so zu behandeln, als hätte er den Typ Zieltyp.

Nur die folgenden Konvertierungen können mit reinterpret_cast durchgeführt werden, es sei denn, solche Konvertierungen entfernen constness (oder Volatilität).

1) Ein Ausdruck eines integralen Typs, Aufzählungstyps, Zeigertyps oder Zeigers auf ein Mitglied kann in seinen eigenen Typ konvertiert werden. Der resultierende Wert ist derselbe wie der Wert des Ausdrucks.
2) Ein Zeiger kann in jeden integralen Typ konvertiert werden, der groß genug ist, um alle Werte seines Typs aufzunehmen (z. B. in std::uintptr_t).
3) Ein Wert eines beliebigen integralen oder Aufzählungstyps kann in einen Zeigertyp konvertiert werden. Ein Zeiger, der in eine Ganzzahl ausreichender Größe konvertiert und zurück in denselben Zeigertyp konvertiert wird, hat garantiert seinen ursprünglichen Wert. Andernfalls kann der resultierende Zeiger nicht sicher dereferenziert werden (die Rundreise-Konvertierung in die entgegengesetzte Richtung ist nicht garantiert; derselbe Zeiger kann mehrere Ganzzahlendarstellungen haben). Die Nullzeiger-Konstante NULL oder die Ganzzahl Null garantiert nicht, dass sie den Nullzeigerwert des Zieltyps ergibt; static_cast oder implizite Konvertierung sollten für diesen Zweck verwendet werden.
4) Jeder Wert vom Typ std::nullptr_t, einschließlich nullptr, kann in jeden integralen Typ konvertiert werden, als ob er (void*)0 wäre, aber kein Wert, nicht einmal nullptr, kann in std::nullptr_t konvertiert werden: static_cast sollte für diesen Zweck verwendet werden.
(seit C++11)
5) Jeder Objektzeigertyp T1* kann in einen anderen Objektzeigertyp cv T2* konvertiert werden. Dies ist exakt äquivalent zu static_cast<cv T2*>(static_cast<cv void*>(Ausdruck)) (was impliziert, dass sich der Wert des Zeigers nicht ändert, wenn die Ausrichtung von T2 nicht strenger ist als die von T1, und die Konvertierung des resultierenden Zeigers zurück in seinen ursprünglichen Typ den ursprünglichen Wert ergibt). In jedem Fall darf der resultierende Zeiger nur sicher dereferenziert werden, wenn der dereferenzierte Wert typzugänglich ist.
6) Ein lvalue(bis C++11)glvalue(seit C++11)-Ausdruck vom Typ T1 kann in eine Referenz auf einen anderen Typ T2 konvertiert werden. Das Ergebnis ist das von *reinterpret_cast<T2*>(p), wobei p ein Zeiger vom Typ "Zeiger auf T1" auf das Objekt oder die Funktion ist, auf die der Ausdruck verweist. Kein temporäres Objekt wird materialisiert oder(seit C++17) erstellt, keine Kopie wird gemacht, keine Konstruktoren oder Konvertierungsfunktionen werden aufgerufen. Die resultierende Referenz kann nur sicher aufgerufen werden, wenn sie typzugänglich ist.
7) Jeder Zeiger auf eine Funktion kann in einen Zeiger auf einen anderen Funktionstyp konvertiert werden. Das Ergebnis ist nicht spezifiziert, aber die Konvertierung eines solchen Zeigers zurück in einen Zeiger auf den ursprünglichen Funktionstyp ergibt den Zeiger auf die ursprüngliche Funktion. Der resultierende Zeiger kann nur sicher aufgerufen werden, wenn sein Funktionstyp aufrufkompatibel mit dem ursprünglichen Funktionstyp ist.
8) Auf einigen Implementierungen (insbesondere auf jedem POSIX-kompatiblen System, wie von dlsym gefordert) kann ein Funktionszeiger in void* oder einen anderen Objektzeiger konvertiert werden, oder umgekehrt. Wenn die Implementierung Konvertierungen in beide Richtungen unterstützt, ergibt die Konvertierung in den ursprünglichen Typ den ursprünglichen Wert. Andernfalls kann der resultierende Zeiger nicht sicher dereferenziert oder aufgerufen werden.
9) Der Nullzeigerwert eines beliebigen Zeigertyps kann in jeden anderen Zeigertyp konvertiert werden, was zum Nullzeigerwert dieses Typs führt. Beachten Sie, dass die Nullzeiger-Konstante nullptr oder jeder andere Wert vom Typ std::nullptr_t nicht mit reinterpret_cast in einen Zeiger konvertiert werden kann: implizite Konvertierung oder static_cast sollten für diesen Zweck verwendet werden.
10) Ein Zeiger auf eine Memberfunktion kann in einen Zeiger auf eine andere Memberfunktion eines anderen Typs konvertiert werden. Die Konvertierung zurück in den ursprünglichen Typ ergibt den ursprünglichen Wert, andernfalls kann der resultierende Zeiger nicht sicher verwendet werden.
11) Ein Zeiger auf ein Objektmitglied einer Klasse T1 kann in einen Zeiger auf ein anderes Objektmitglied einer anderen Klasse T2 konvertiert werden. Wenn die Ausrichtung von T2 nicht strenger ist als die von T1, ergibt die Konvertierung zurück in den ursprünglichen Typ T1 den ursprünglichen Wert, andernfalls kann der resultierende Zeiger nicht sicher verwendet werden.

Wie bei allen Cast-Ausdrücken ist das Ergebnis

  • ein lvalue, wenn Zieltyp ein lvalue-Referenztyp ist oder ein rvalue-Referenztyp auf eine Funktion(seit C++11);
  • ein xvalue, wenn Zieltyp ein rvalue-Referenztyp auf ein Objekt ist;
(seit C++11)
  • sonst ein prvalue.

[bearbeiten] Typ-Aliasing

[bearbeiten] Typ-Zugriff

Wenn ein Typ T_ref ähnlich zu einem der folgenden Typen ist, ist ein Objekt mit dem dynamischen Typ T_obj durch ein lvalue(bis C++11)glvalue(seit C++11) vom Typ T_ref *typzugänglich*

  • char
  • unsigned char
  • std::byte
(seit C++17)
  • T_obj
  • der vorzeichenbehaftete oder vorzeichenlose Typ, der T_obj entspricht

Wenn ein Programm versucht, den gespeicherten Wert eines Objekts durch ein lvalue(bis C++11)glvalue(seit C++11) zu lesen oder zu ändern, durch das es nicht typzugänglich ist, ist das Verhalten undefiniert.

Diese Regel ermöglicht eine typbasierte Alias-Analyse, bei der ein Compiler annimmt, dass der Wert, der über ein glvalue eines Typs gelesen wird, nicht durch eine Schreiboperation auf ein glvalue eines anderen Typs geändert wird (vorbehaltlich der oben genannten Ausnahmen).

Beachten Sie, dass viele C++-Compiler diese Regel als nicht standardmäßige Spracherweiterung lockern, um den Zugriff auf den inaktiven Member einer union mit falschem Typ zu gestatten (ein solcher Zugriff ist in C nicht undefiniert).

[bearbeiten] Aufrufkompatibilität

Wenn eine der folgenden Bedingungen erfüllt ist, ist ein Typ T_call *aufrufkompatibel* mit einem Funktionstyp T_func

  • T_call ist derselbe Typ wie T_func.
(seit C++17)

Wenn eine Funktion über einen Ausdruck aufgerufen wird, dessen Funktionstyp nicht aufrufkompatibel mit dem Typ der Definition der aufgerufenen Funktion ist, ist das Verhalten undefiniert.

[bearbeiten] Anmerkungen

Unter der Annahme, dass die Ausrichtungsanforderungen erfüllt sind, ändert ein reinterpret_cast den Wert eines Zeigers nicht, außer in einigen wenigen begrenzten Fällen, die zeigerinterkonvertierbare Objekte betreffen.

struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // not standard-layout
union U { int a; double b; } u = {0};
int arr[2];
 
int* p1 = reinterpret_cast<int*>(&s1); // value of p1 is "pointer to s1.a" because
                                       // s1.a and s1 are pointer-interconvertible
 
int* p2 = reinterpret_cast<int*>(&s2); // value of p2 is unchanged by reinterpret_cast
                                       // and is "pointer to s2". 
 
int* p3 = reinterpret_cast<int*>(&u);  // value of p3 is "pointer to u.a":
                                       // u.a and u are pointer-interconvertible
 
double* p4 = reinterpret_cast<double*>(p3); // value of p4 is "pointer to u.b": u.a and
                                            // u.b are pointer-interconvertible because
                                            // both are pointer-interconvertible with u
 
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast
                                        // and is "pointer to arr"

Das Durchführen eines Klassenmemberzugriffs, der einen nicht-statischen Datenmember oder eine nicht-statische Memberfunktion bezeichnet, auf ein glvalue, das tatsächlich kein Objekt des entsprechenden Typs bezeichnet – z. B. eines, das durch einen reinterpret_cast erhalten wurde – führt zu undefiniertem Verhalten.

struct S { int x; };
struct T { int x; int f(); };
struct S1 : S {};    // standard-layout
struct ST : S, T {}; // not standard-layout
 
S s = {};
auto p = reinterpret_cast<T*>(&s); // value of p is "pointer to s"
auto i = p->x; // class member access expression is undefined behavior;
               // s is not a T object
p->x = 1; // undefined behavior
p->f();   // undefined behavior
 
S1 s1 = {};
auto p1 = reinterpret_cast<S*>(&s1); // value of p1 is "pointer to the S subobject of s1"
auto i = p1->x; // OK
p1->x = 1;      // OK
 
ST st = {};
auto p2 = reinterpret_cast<S*>(&st); // value of p2 is "pointer to st"
auto i = p2->x; // undefined behavior
p2->x = 1;      // undefined behavior

Viele Compiler geben in solchen Fällen "Strict Aliasing"-Warnungen aus, obwohl solche Konstrukte technisch gesehen etwas anderes verletzen als der Absatz, der üblicherweise als "Strict Aliasing Rule" bezeichnet wird.

Der Zweck von Strict Aliasing und verwandten Regeln ist die Ermöglichung einer typbasierten Alias-Analyse, die dezimiert würde, wenn ein Programm gültig eine Situation schaffen könnte, in der zwei Zeiger auf nicht verwandte Typen (z. B. ein int* und ein float*) gleichzeitig existieren und beide verwendet werden könnten, um denselben Speicher zu lesen oder zu schreiben (siehe diese E-Mail auf dem SG12-Reflektor). Daher führt jede Technik, die scheinbar in der Lage ist, eine solche Situation zu schaffen, notwendigerweise zu undefiniertem Verhalten.

Wenn die Bytes eines Objekts als Wert eines anderen Typs interpretiert werden müssen, können std::memcpy oder std::bit_cast(seit C++20) verwendet werden.

double d = 0.1;
std::int64_t n;
static_assert(sizeof n == sizeof d);
// n = *reinterpret_cast<std::int64_t*>(&d); // Undefined behavior
std::memcpy(&n, &d, sizeof d);               // OK
n = std::bit_cast<std::int64_t>(d);          // also OK

Wenn die Implementierung std::intptr_t und/oder std::uintptr_t bereitstellt, dann ist eine Konvertierung von einem Zeiger auf einen Objekttyp oder cv void in diese Typen immer wohl-definiert. Dies ist jedoch für einen Funktionszeiger nicht garantiert.

(seit C++11)

In C greifen Aggregatkopien und -zuweisungen auf das Aggregatobjekt als Ganzes zu. Aber in C++ werden solche Aktionen immer über einen Memberfunktionsaufruf durchgeführt, der auf die einzelnen Unterobjekte zugreift, anstatt auf das gesamte Objekt (oder im Falle von Unions wird die Objektrepräsentation kopiert, d. h. über unsigned char).

[bearbeiten] Schlüsselwörter

reinterpret_cast

[bearbeiten] Beispiel

Demonstriert einige Verwendungen von reinterpret_cast

#include <cassert>
#include <cstdint>
#include <iostream>
 
int f() { return 42; }
 
int main()
{
    int i = 7;
 
    // pointer to integer and back
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast is an error
    std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n';
    int* p1 = reinterpret_cast<int*>(v1);
    assert(p1 == &i);
 
    // pointer to function to another and back
    void(*fp1)() = reinterpret_cast<void(*)()>(f);
    // fp1(); undefined behavior
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
    std::cout << std::dec << fp2() << '\n'; // safe
 
    // type aliasing through pointer
    char* p2 = reinterpret_cast<char*>(&i);
    std::cout << (p2[0] == '\x7' ? "This system is little-endian\n"
                                 : "This system is big-endian\n");
 
    // type aliasing through reference
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
 
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast<int&>(
    //     const_iref); // compiler error - can't get rid of const
    // Must use const_cast instead: int &iref = const_cast<int&>(const_iref);
}

Mögliche Ausgabe

The value of &i is 0x7fff352c3580
42
This system is little-endian
42

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 195 C++98 Konvertierung zwischen Funktionszeigern
und Objektzeigern nicht erlaubt
Wurde bedingt unterstützt gemacht
CWG 658 C++98 das Ergebnis von Zeigerkonvertierungen war nicht spezifiziert
(außer für Konvertierungen zurück zum ursprünglichen Typ)
Spezifikation für Zeiger bereitgestellt,
deren Zeigertypen erfüllen
die Ausrichtungsanforderungen
CWG 799 C++98 es war unklar, welche Identitätskonvertierung
mit reinterpret_cast durchgeführt werden kann
wurde klargestellt
CWG 1268 C++11 reinterpret_cast konnte nur
lvalues in Referenztypen konvertieren
xvalues auch erlaubt
CWG 2780 C++98 reinterpret_cast konnte
Funktions-lvalues nicht in andere Referenztypen konvertieren
erlaubt
CWG 2939 C++17 reinterpret_cast konnte
prvalues in rvalue-Referenztypen konvertieren
nicht erlaubt

[bearbeiten] Referenzen

  • C++23 Standard (ISO/IEC 14882:2024)
  • 7.6.1.10 Reinterpret cast [expr.reinterpret.cast]
  • C++20 Standard (ISO/IEC 14882:2020)
  • 7.6.1.9 Reinterpret cast [expr.reinterpret.cast]
  • C++17 Standard (ISO/IEC 14882:2017)
  • 8.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++14 Standard (ISO/IEC 14882:2014)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++11 Standard (ISO/IEC 14882:2011)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++98 Standard (ISO/IEC 14882:1998)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++03-Standard (ISO/IEC 14882:2003)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]

[bearbeiten] Siehe auch

const_cast-Konvertierung fügt const hinzu oder entfernt es[bearbeiten]
static_cast-Konvertierung führt grundlegende Konvertierungen durch[bearbeiten]
dynamic_cast-Konvertierung führt überprüfte polymorphe Konvertierungen durch[bearbeiten]
explizite Konvertierungen permissive Konvertierungen zwischen Typen [bearbeiten]
Standardkonvertierungen implizite Konvertierungen von einem Typ zu einem anderen[bearbeiten]
(C++20)
interpretiert die Objekt-Repräsentation eines Typs als die eines anderen Typs neu
(Funktion-Template) [bearbeiten]