631 Stimmen

Wann ist reinterpret_cast zu verwenden?

Ich bin etwas verwirrt über die Anwendbarkeit von reinterpret_caststatic_cast . Nach dem, was ich gelesen habe, sind die allgemeinen Regeln statische Besetzung zu verwenden, wenn die Typen zur Kompilierzeit interpretiert werden können, daher das Wort static . Dies ist der Cast, den der C++-Compiler intern auch für implizite Casts verwendet.

reinterpret_cast s sind in zwei Szenarien anwendbar:

  • Ganzzahlige Typen in Zeigertypen umwandeln und umgekehrt
  • einen Zeigertyp in einen anderen umwandeln. Die allgemeine Vorstellung, die ich bekomme, ist, dass dies unportabel ist und vermieden werden sollte.

Wo ich ein wenig verwirrt bin, ist eine Verwendung, die ich benötige, ich rufe C++ von C und der C-Code muss auf das C++-Objekt halten, so dass im Grunde hält es eine void* . Welcher Cast sollte verwendet werden, um zwischen den void * und den Klassentyp?

Ich habe die Verwendung von beidem gesehen static_cast y reinterpret_cast ? Doch nach dem, was ich gelesen habe, scheint es static besser ist, da der Cast zur Kompilierzeit erfolgen kann? Obwohl es heißt, man solle reinterpret_cast um von einem Zeigertyp in einen anderen umzuwandeln?

30 Stimmen

reinterpret_cast findet nicht zur Laufzeit statt. Beides sind Anweisungen zur Kompilierzeit. Von de.cppreference.com/w/cpp/language/reinterpret_cast : "Im Gegensatz zu static_cast, aber wie const_cast, wird der Ausdruck reinterpret_cast nicht in CPU-Anweisungen kompiliert. Es handelt sich um eine reine Compilerdirektive, die den Compiler anweist, die Bitfolge (Objektdarstellung) des Ausdrucks so zu behandeln, als hätte sie den Typ new_type."

0 Stimmen

@HeretoLearn, ist es möglich, die relevanten Codestücke aus der *.c und *.cpp Datei hinzuzufügen? Ich denke, das kann die Aussagekraft der Frage verbessern.

6voto

TRPh Punkte 169

Hier ist eine Variante des Programms von Avi Ginsburg, die die Eigenschaft von reinterpret_cast die von Chris Luengo, flodin und cmdLP erwähnt wurde: dass der Compiler die Speicherstelle, auf die gezeigt wird, so behandelt, als wäre sie 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);
}

Das Ergebnis ist eine Ausgabe wie diese:

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 zu erkennen, dass zuerst das B-Objekt als B-spezifische Daten im Speicher angelegt wird, gefolgt von dem eingebetteten A-Objekt. Die static_cast gibt korrekt die Adresse des eingebetteten A-Objekts zurück, und der Zeiger, der durch static_cast gibt den Wert des Datenfeldes korrekt an. Der Zeiger, der von reinterpret_cast behandelt b Wenn der Zeiger also versucht, das Datenfeld abzurufen, gibt er einige B-spezifische Daten zurück, als ob es sich um den Inhalt dieses Feldes handeln würde.

Eine Verwendung von reinterpret_cast ist die Umwandlung eines Zeigers in eine ganze Zahl ohne Vorzeichen (wenn Zeiger und ganze Zahlen ohne Vorzeichen die gleiche Größe haben):

int i; unsigned int u = reinterpret_cast<unsigned int>(&i);

2 Stimmen

Alles hier, außer dem letzten Beispiel, ist undefiniertes Verhalten; es ist nur als (nicht zuverlässiges) Mittel zur Veranschaulichung von Implementierungsdetails der Sprache interessant.

3voto

Martin R. Punkte 67

Sie könnten reinterprete_cast verwenden, um die Vererbung zur Kompilierzeit zu prüfen.
Siehe hier: Verwendung von reinterpret_cast zur Überprüfung der Vererbung zur Kompilierzeit

3voto

cmdLP Punkte 1460

Zunächst haben Sie einige Daten in einem bestimmten Typ wie hier int:

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

Dann wollen Sie auf die gleiche Variable als einen anderen Typ wie float zugreifen: Sie können sich entscheiden zwischen

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

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

ou

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

//this could be used in c and cpp

BRIEF: Das bedeutet, dass der gleiche Speicher als ein anderer Typ verwendet wird. So können Sie binäre Darstellungen von Floats als int-Typ wie oben in Floats umwandeln. 0x80000000 ist z.B. -0 (Mantisse und Exponent sind Null, aber das Vorzeichen, die msb, ist Eins. Dies funktioniert auch für Doubles und Long Doubles.

OPTIMIEREN: Ich denke, reinterpret_cast würde in vielen Compilern optimiert werden, 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 gecasteten Wert vor dem Casting in einer Variablen speichern! Dieses Makro kann dabei helfen:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })

1 Stimmen

Es ist wahr, dass "es bedeutet, dass der gleiche Speicher als ein anderer Typ verwendet wird", aber es ist auf ein bestimmtes Paar von Typen beschränkt. In Ihrem Beispiel reinterpret_cast Formular int a float& ist undefiniertes Verhalten.

0 Stimmen

Compiler optimieren memcpy auf reine Register-Operationen zu beschränken, wenn dies möglich ist; die Übertragungen sind einfach (aber auch UB - wenn der Wert gebraucht - wie überall auf dieser Seite betont wird).

1voto

template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

Ich habe versucht, zu schließen und schrieb eine einfache sichere Besetzung mit Vorlagen. Beachten Sie, dass diese Lösung nicht garantieren, Zeiger auf eine Funktion zu werfen.

2 Stimmen

Was? Wozu die Mühe? Das ist genau das, was reinterpret_cast in dieser Situation bereits tut: "Ein Objektzeiger kann explizit in einen Objektzeiger eines anderen Typs umgewandelt werden.[72] Wenn ein prvalue v vom Typ Objektzeiger wird in den Objektzeigertyp "Zeiger auf" umgewandelt cv T ", so lautet das Ergebnis static_cast<cv T*>(static_cast<cv void*>(v)) ." -- N3797.

0 Stimmen

In Bezug auf c++2003 Standard kann ich NICHT feststellen, dass reinterpret_cast する static_cast<cv T*>(static_cast<cv void*>(v))

1 Stimmen

OK, das stimmt, aber ich kümmere mich nicht um eine Version von vor 13 Jahren, und die meisten Programmierer sollten das auch nicht, wenn sie es (wie wahrscheinlich) vermeiden können. Antworten und Kommentare sollten wirklich den neuesten verfügbaren Standard widerspiegeln, sofern nicht anders angegeben... IMHO. Wie dem auch sei, ich denke, der Ausschuss hielt es für notwendig, dies nach 2003 ausdrücklich hinzuzufügen. (denn, wenn ich mich recht erinnere, war es in C++11 genauso)

-10voto

Marius K Punkte 450

Schnelle Antwort: Verwenden Sie static_cast wenn es sich kompilieren lässt, andernfalls greifen Sie auf reinterpret_cast .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X