Perchè questo reinterpret_cast non compila?
-
18-09-2019 - |
Domanda
Capisco che reinterpret_cast
è pericoloso, sto solo facendo questo per testarlo. Ho il seguente codice:
int x = 0;
double y = reinterpret_cast<double>(x);
Quando provo a compilare il programma, mi dà un errore che dice
Cast valida dal tipo 'float' di tipo 'double
Che cosa sta succedendo? Ho pensato che fosse il cast reinterpret_cast
canaglia che si potrebbe usare per convertire le mele ai sottomarini, perché questa semplice cast non compilerà?
Soluzione
Con l'assegnazione y al valore restituito dal cast non siete realmente lanciare il x
valore, si sta convertendo esso. Cioè, y
non punta a x
e fingere che punti ad un galleggiante. Conversione costruisce un nuovo valore di tipo float
e assegna il valore da x
. Ci sono diversi modi per fare questa conversione in C ++, tra i quali:
int main()
{
int x = 42;
float f = static_cast<float>(x);
float f2 = (float)x;
float f3 = float(x);
float f4 = x;
return 0;
}
L'unica vera differenza è l'ultimo (una conversione implicita) genererà un compilatore di diagnosi sui livelli di pericolo più elevati. Ma fanno tutti funzionalmente la stessa cosa - e in molti casi effettivamente la stessa cosa, come lo stesso codice macchina
. Ora, se davvero si vuole far finta che x
è un galleggiante, allora davvero si vuole lanciare x
, in questo modo:
#include <iostream>
using namespace std;
int main()
{
int x = 42;
float* pf = reinterpret_cast<float*>(&x);
(*pf)++;
cout << *pf;
return 0;
}
Si può vedere quanto sia pericoloso questo è. Infatti, l'output quando eseguire questo sulla mia macchina è 1
, che è decisamente poco 42 + 1.
Altri suggerimenti
In C ++ reinterpret_cast
può eseguire solo un insieme specifico di conversioni, esplicitamente elencati nella specifica del linguaggio. In breve, reinterpret_cast
può eseguire solo le conversioni puntatore a puntatore e riferimento to riferimento conversioni (più puntatore a intero e conversioni intero-to-puntatore). Ciò è coerente con l'intento espresso nel nome del cast:. Esso è destinato ad essere utilizzato per la reinterpretazione puntatore / riferimento
Quello che si sta cercando di fare non è reinterpretazione. Se si vuole reinterpretare un int
come double
ci si deve convertirlo in un tipo di riferimento
double y = reinterpret_cast<double&>(x);
anche se l'equivalente reinterpretazione puntatore-based è probabilmente più esplicito
double y = *reinterpret_cast<double*>(&x); // same as above
Nota però, che mentre reinterpret_cast
può convertire i tipi di riferimento / puntatore, il tentativo effettivo di leggere i dati attraverso il riferimento risultante / puntatore produce un comportamento indefinito.
E in ogni caso questo, naturalmente, non può avere molto senso su una piattaforma con int
e double
di diverse dimensioni (in quanto in caso di double
più grande potrete leggere al di là della memoria occupata da x
).
Così, alla fine, tutto si riduce a quello che stavi cercando di raggiungere. Memoria reinterpretazione? Vedi sopra. Una specie di int
più significativo per double
conversione? Se è così, reinterpret_cast
non vi aiuterà qui.
reinterpret_cast non è un cast generale. Secondo la sezione spec C ++ 03 5.2.10.1:
Le conversioni che possono essere eseguite in modo esplicito utilizzando reinterpret_cast sono elencati di seguito. Nessun'altra conversione può essere eseguita in modo esplicito utilizzando reinterpret_cast.
E non c'è nulla elencato che descrive la conversione tra tipi di punti integrali e galleggianti (o tra tipi integrali, anche questo è reinterpret_cast<long>(int(3));
illegale)
Se si sta tentando di convertire i bit del vostro int
a una rappresentazione di un double
, è necessario lanciare il Indirizzo non il valore. È inoltre necessario assicurarsi che le dimensioni corrispondono:
uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
Il compilatore rifiuta quello che hai scritto come una sciocchezza, perché int
e double
possono essere oggetti di diverse dimensioni. Si potrebbe ottenere lo stesso effetto in questo modo, anche se è certamente pericoloso:
int x = 0;
double y = *reinterpret_cast<double*>(&x);
Questa è potenzialmente pericoloso, perché se x
e y
sono formati differenti (diciamo int
è di quattro byte e double
è di otto byte) allora quando dereference gli otto byte di memoria a &x
compilare y
si accederà quattro byte di x
e quattro byte di ... tutto ciò che viene dopo in memoria (forse l'inizio di y
, o spazzatura, o qualcosa di completamente.)
Se si desidera convertire un intero ad un doppio, utilizzare un static_cast
e sarà eseguire la conversione.
Se si desidera accedere alla sequenza di bit di x
, gettato a qualche tipo comodo puntatore (diciamo, byte*
) e l'accesso fino a sizeof(int) / sizeof(byte)
:
byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
// do something with p[i]
}
reinterpretare fusione permette di reinterpretare un blocco di memoria come un tipo diverso. Questo deve essere eseguita su puntatori o riferimenti :
int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f ); // !!
L'altra cosa è che è in realtà un cast molto pericoloso, non solo a causa di valori anomali come risultati, o l'asserzione di cui sopra non mancanza, ma perché se i tipi sono di dimensioni diverse, e si reinterpretano da ' sorgente' a 'tipi di destinazione', qualsiasi operazione sul riferimento / puntatore reinterpretato accederà byte sizeof(destination)
. Se sizeof(destination)>sizeof(source)
allora che farà un passo oltre la memoria variabile reale, potenzialmente uccidere l'applicazione o overwritting altre variabili diverse dalla sorgente o destinazione:
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.
asswet( t.y != 20 );
reinterpret_cast
è meglio utilizzato per i puntatori. Quindi un puntatore ad un oggetto può essere trasformato in un "sottomarino".
MSDN :
L'operatore può essere reinterpret_cast utilizzati per le conversioni come char * a int * o * a One_class Unrelated_class *, che sono intrinsecamente non sicuri.
Il risultato di un reinterpret_cast Non può tranquillamente essere utilizzato per qualsiasi cosa oltre ad essere gettato al suo tipo di originale. Altri usi sono, meglio, non portabile.
L'approccio cr mi ha portato giù un percorso strano con risultati incostanti. Alla fine ho trovato molto meglio memcpy come questo!
double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));
Casting un int ad un doppio non richiede un cast. Il compilatore eseguirà l'assegnazione implicitamente.
Il reinterpret_cast viene utilizzato con puntatori e riferimenti, per esempio, che esprimono un int *
ad un double *
.
Questo è interessante. Forse si sta facendo una conversione implicita da int a stare a galla prima di tentare il cast di raddoppiare. int e galleggiano tipi tendono ad avere la stessa dimensione in byte (a seconda del sistema, naturalmente).