Warum wird dieser reinterpret_cast nicht kompiliert?

70

Ich verstehe, dass reinterpret_castdas gefährlich ist, ich mache das nur, um es zu testen. Ich habe folgenden Code:

int x = 0;
double y = reinterpret_cast<double>(x);

Wenn ich versuche, das Programm zu kompilieren, wird eine Fehlermeldung angezeigt

ungültige Umwandlung von Typ 'float' in Typ 'double

Was ist los? Ich dachte, reinterpret_castes wäre die Schurkenbesetzung, mit der man Äpfel in U-Boote umwandeln könnte. Warum wird diese einfache Besetzung nicht kompiliert?

Vlad der Impala
quelle
Ich habe dies noch nie in C ++ versucht, also rate ich nur. Funktioniert es, wenn Sie schweben? Könnte es nicht gefallen, dass die beiden Typen eine unterschiedliche Bitlänge haben?
Chris Pitman
" reinterpret_cast<double>(x)Was haben Sie erwartet, dass dieser Ausdruck tut?"
Neugieriger
3
int ist 32 Bit. double ist 64 Bit. Das könnte das Problem sein. Kannst du überprüfen?
user4951
Sind Sie sicher, dass sich der Compiler darüber beschwert float? floatIhr Code-Snippet enthält keine .
Ruslan

Antworten:

47

Vielleicht reinterpret_castist der Rouge-Operator, der Zeiger in Äpfel als Zeiger auf U-Boote "umwandeln" kann, eine bessere Denkweise .

Indem Sie dem von der Besetzung zurückgegebenen Wert y zuweisen, wandeln Sie den Wert nicht wirklich um x, sondern konvertieren ihn. Das heißt, yzeigt nicht auf xund gibt vor, dass es auf einen Schwimmer zeigt. Die Konvertierung erstellt einen neuen Wert vom Typ floatund weist ihm den Wert von zu x. Es gibt verschiedene Möglichkeiten, diese Konvertierung in C ++ durchzuführen, darunter:

int main()
{
    int x = 42;
    float f = static_cast<float>(x);
    float f2 = (float)x;
    float f3 = float(x);
    float f4 = x;
    return 0;
}

Der einzige wirkliche Unterschied besteht darin, dass der letzte (eine implizite Konvertierung) eine Compilerdiagnose für höhere Warnstufen generiert. Aber sie alle tun funktional dasselbe - und in vielen Fällen sogar dasselbe wie im selben Maschinencode.

Wenn Sie wirklich so tun möchten, xals wäre dies ein Float, dann möchten Sie wirklich Folgendes xtun:

#include <iostream>
using namespace std;

int main()
{
    int x = 42;
    float* pf = reinterpret_cast<float*>(&x);
    (*pf)++;
    cout << *pf;
    return 0;
}

Sie können sehen, wie gefährlich das ist. Tatsächlich ist die Ausgabe, wenn ich dies auf meinem Computer ausführe 1, definitiv nicht 42 + 1.

John Dibling
quelle
will (float) x gibt 42 oder eine binäre Darstellung eines nicht verwandten Doppels zurück. Ich denke, dieser Typ bedeutet Neuinterpretation und das ist es, was er will.
user4951
(float)xführt eine Konvertierung durch, keine Besetzung.
John Dibling
2
Ihr Code unten ist eine strikte Aliasing-Verletzung und weist daher ein undefiniertes Verhalten auf.
Maciej Piechotka
Wenn ich so etwas in Low-Level-Code mache, ist es häufig so, dass die Bits ohne irgendeine Art von Konvertierung angezeigt werden (außer für druckbares Hex). Zum Beispiel beim (De-) Serialisieren von Datenstrukturen, die mit einem Mikrocontroller ausgetauscht wurden. Denken Sie an "Debugger".
Technophile
50

In C ++ reinterpret_castkönnen nur bestimmte Konvertierungen durchgeführt werden, die in der Sprachspezifikation explizit aufgeführt sind. Kurz gesagt, reinterpret_castkann nur Zeiger-zu-Zeiger-Konvertierungen und Referenz-zu-Referenz-Konvertierungen durchführen (plus Zeiger-zu-Ganzzahl- und Ganzzahl-zu-Zeiger-Konvertierungen). Dies steht im Einklang mit der Absicht, die im Namen der Besetzung zum Ausdruck kommt: Sie soll für die Neuinterpretation von Zeigern / Referenzen verwendet werden.

Was Sie versuchen, ist keine Neuinterpretation. Wenn Sie eine intals neu interpretieren möchten, müssen doubleSie sie in einen Referenztyp konvertieren

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

obwohl die äquivalente zeigerbasierte Neuinterpretation wahrscheinlich expliziter ist

double y = *reinterpret_cast<double*>(&x); // same as above

Beachten Sie jedoch, dass reinterpret_castder tatsächliche Versuch, die Daten über die resultierende Referenz / den resultierenden Zeiger zu lesen , zwar zu einer Konvertierung der Referenz- / Zeigertypen führt, jedoch zu einem undefinierten Verhalten führt.

Und auf jeden Fall kann dies auf einer Plattform mit intund doubleunterschiedlicher Größe nicht viel Sinn machen (da Sie bei größeren Plattformen doubleüber den von gelesenen Speicher hinaus lesen werden x).

Am Ende läuft alles auf das hinaus, was Sie erreichen wollten. Neuinterpretation des Gedächtnisses? Siehe oben. Eine Art von Bedeutung intfür die doubleKonvertierung? Wenn ja, reinterpret_castwird Ihnen hier nicht helfen.

Ameise
quelle
1
reinterpret_cast can only perform pointer-to-pointer conversions and reference-to-reference conversions (plus pointer-to-integer and integer-to-pointer conversions)Dies drückte die Frage flach und konnte als Antwort akzeptiert werden.
RaGa__M
12

reinterpret_cast ist keine allgemeine Besetzung. Gemäß der C ++ 03-Spezifikation Abschnitt 5.2.10.1:

Konvertierungen, die explizit mit reinterpret_cast durchgeführt werden können, sind unten aufgeführt. Mit reinterpret_cast kann keine andere Konvertierung explizit durchgeführt werden.

Und es ist nichts aufgeführt, was die Konvertierung zwischen Integral- und Gleitkommatypen beschreibt (oder zwischen Integraltypen, auch wenn dies illegal ist reinterpret_cast<long>(int(3));).

R Samuel Klatchko
quelle
11

Wenn Sie versuchen, die Bits von inta in die Darstellung von a umzuwandeln double, müssen Sie die Adresse und nicht den Wert umwandeln . Sie müssen auch sicherstellen, dass die Größen übereinstimmen:

uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
finnw
quelle
4

Der Compiler lehnt das, was Sie als Unsinn geschrieben haben, ab intund doublekann Objekte mit unterschiedlichen Größen sein. Sie könnten den gleichen Effekt auf diese Weise erzielen, obwohl dies sicherlich gefährlich ist:

int x = 0;
double y = *reinterpret_cast<double*>(&x);

Es ist sehr gefährlich , denn wenn xund ysind diffrent Größen (sagen wir mal intvier Bytes und doubleist acht Byte) dann , wenn Sie die acht Byte Speicher dereferenzieren bei &xausfüllen ySie vier Bytes zugreifen xund vier Bytes von ... , was als nächstes kommt im Gedächtnis (möglicherweise der Anfang yoder Müll oder etwas ganz anderes.)

Wenn Sie eine Ganzzahl in ein Double konvertieren möchten, verwenden Sie a static_castund es wird eine Konvertierung durchgeführt.

Wenn Sie auf das Bitmuster von zugreifen möchten x, wandeln Sie es in einen geeigneten Zeigertyp (z. B. byte*) um und greifen Sie auf zu sizeof(int) / sizeof(byte):

byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
  // do something with p[i]
}
Dominic Cooney
quelle
2
Nicht der Grund, warum der Compiler es ablehnt, aber die Diskussion über Typgrößen ist wertvoll.
David Rodríguez - Dribeas
3

Mit Cast neu interpretieren können Sie einen Speicherblock als einen anderen Typ interpretieren. Dies muss an Zeigern oder Referenzen durchgeführt werden :

int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f );   // !!

Die andere Sache ist, dass es sich in der Tat um eine ziemlich gefährliche Besetzung handelt, nicht nur, weil seltsame Werte als Ergebnis herauskommen oder die obige Behauptung nicht fehlschlägt, sondern weil die Typen unterschiedlich groß sind und Sie von 'Quelle' zu 'neu' interpretieren 'Ziel'-Typen, jede Operation an der neu interpretierten Referenz / dem neu interpretierten Zeiger greift auf sizeof(destination)Bytes zu. Wenn dies sizeof(destination)>sizeof(source)dann über den tatsächlichen Variablenspeicher hinausgeht und möglicherweise Ihre Anwendung beendet oder andere Variablen als die Quelle oder das Ziel überschreibt:

struct test {
   int x;
   int y;
};
test t = { 10, 20 };
double & d = reinterpret_cast<double&>( t.x );
d = 1.0/3.0;
assert( t.x != 10 ); // most probably at least.
assert( t.y != 20 );
David Rodríguez - Dribeas
quelle
1

reinterpret_castwird am besten für Zeiger verwendet. So kann ein Zeiger auf ein Objekt in ein "U-Boot" verwandelt werden.

Von msdn :

Der Operator reinterpret_cast kann für Konvertierungen wie char * in int * oder One_class * in Unrelated_class * verwendet werden, die von Natur aus unsicher sind.

Das Ergebnis eines reinterpret_cast kann nur für den ursprünglichen Typ verwendet werden. Andere Verwendungen sind bestenfalls nicht portierbar.

Alex B.
quelle
0

Das Umwandeln eines Int in ein Double erfordert keine Umwandlung. Der Compiler führt die Zuweisung implizit aus.

Der reinterpret_cast wird mit Zeigern und Referenzen verwendet, z. B. beim Casting von int *an a double *.

Matt Davis
quelle
0

Das ist interessant. Vielleicht wird eine implizite Konvertierung von int in float durchgeführt, bevor versucht wird, die Besetzung zu verdoppeln. int- und float-Typen haben in Byte normalerweise die gleiche Größe (abhängig von Ihrem System natürlich).

Andy White
quelle
0

Die Neuinterpretation führte mich auf einen seltsamen Weg mit inkonsistenten Ergebnissen. Am Ende fand ich es viel besser, so etwas zu merken!

double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));
Evan Moran
quelle
0

Verwenden Sie eine Gewerkschaft. Dies ist die am wenigsten fehleranfällige Methode zur Speicherzuordnung zwischen einer Ganzzahl und einem Gleitkommatyp. Das erneute Interpretieren eines Zeigers führt zu Aliasing-Warnungen.

#include <stdio.h>
#include <stdint.h>

int main(int argc, char *argv[])
{
    union { uint32_t i; float f; } v;  // avoid aliasing rules trouble
    v.i = 42;
    printf("int 42 is float %f\n", v.f);
    v.f = 42.0;
    printf("float 42 is int 0x%08x\n", v.i);
}
MrRickDean
quelle
Nein, die Verwendung einer Union auf diese Weise ist implementierungsspezifisch und daher nicht die am wenigsten fehleranfällige Methode (siehe Zweck von Unions in C und C ++) ). Beispiel: Wenn i32 Bit und f64 Bit sind, ientspricht dies den oberen oder unteren 32 Bits von f?
fcdt
Wenn die Größen nicht übereinstimmen, führt die Neuinterpretation von Zeigern zu noch größeren Problemen, aber Ihr Link ist sehr gut. Ich gebe zu, dass die Gewerkschaften für diesen Zweck nicht gut fundiert sind und wahrscheinlich Portabilitätsprobleme haben, aber diese sind der Aufgabe inhärent.
MrRickDean