¿Por qué no se compila este reinterpret_cast?
-
18-09-2019 - |
Pregunta
Entiendo que reinterpret_cast
Es peligroso, sólo estoy haciendo esto para probarlo.Tengo el siguiente código:
int x = 0;
double y = reinterpret_cast<double>(x);
Cuando intento compilar el programa me da un error que dice
conversión no válida del tipo 'flotante' al tipo 'doble
¿Qué está sucediendo?Pensé reinterpret_cast
era el elenco rebelde que podías usar para convertir manzanas en submarinos, ¿por qué no se compila este elenco simple?
Solución
Mediante la asignación y al valor devuelto por el elenco en realidad no estás echando la x
valor, que está convirtiendo la misma. Es decir, no y
sí apunta a x
y pretender que apunte a un flotador. Conversión construye un nuevo valor de tipo float
y le asigna el valor de x
. Hay varias maneras de hacer esta conversión en C ++, entre ellos:
int main()
{
int x = 42;
float f = static_cast<float>(x);
float f2 = (float)x;
float f3 = float(x);
float f4 = x;
return 0;
}
La única diferencia real es la última (una conversión implícita) generará un compilador de diagnóstico en los niveles más altos de advertencia. Pero todos lo hacen funcionalmente lo mismo - y en muchos casos realmente lo mismo, al igual que en el mismo código de máquina
. Ahora bien, si usted realmente quiere pretender que x
es un flotador, entonces realmente quieres lanzar x
, al hacer esto:
#include <iostream>
using namespace std;
int main()
{
int x = 42;
float* pf = reinterpret_cast<float*>(&x);
(*pf)++;
cout << *pf;
return 0;
}
Se puede ver lo peligroso que es. De hecho, la salida cuando ejecuta este en mi máquina es 1
, que decididamente no es 42 + 1.
Otros consejos
En C++ reinterpret_cast
solo puede realizar un conjunto específico de conversiones, enumeradas explícitamente en la especificación del idioma.En breve, reinterpret_cast
solo puede realizar conversiones de puntero a puntero y conversiones de referencia a referencia (más conversiones de puntero a entero y de entero a puntero).Esto es consistente con la intención expresada en el propio nombre del elenco:está destinado a ser utilizado para la reinterpretación de punteros/referencias.
Lo que estás intentando hacer no es una reinterpretación.Si quieres reinterpretar un int
como un double
Tendrías que convertirlo a un tipo de referencia.
double y = reinterpret_cast<double&>(x);
aunque la reinterpretación equivalente basada en punteros es probablemente más explícita
double y = *reinterpret_cast<double*>(&x); // same as above
Tenga en cuenta, sin embargo, que mientras reinterpret_cast
puede convertir los tipos de referencia/puntero, el intento real de leer los datos a través de la referencia/puntero resultante produce un comportamiento indefinido.
Y en cualquier caso esto, por supuesto, no puede tener mucho sentido en una plataforma con int
y double
de diferente tamaño (ya que en caso de mayor double
Leerás más allá de la memoria ocupada por x
).
Entonces, al final, todo se reduce a lo que intentabas lograr.¿Reinterpretación de la memoria?Véase más arriba.Algún tipo de más significativo int
a double
¿conversión?En ese caso, reinterpret_cast
No te ayudará aquí.
reinterpret_cast no es un reparto general. De acuerdo con la sección de especificaciones C ++ 03 5.2.10.1:
Conversiones que se pueden realizar de forma explícita el uso de reinterpret_cast se enumeran a continuación. Ningún otro cambio se realiza de manera explícita usando reinterpret_cast.
Y no aparece nada que describe la conversión entre tipos de puntos integrales y flotantes (o entre tipos enteros, incluso esto es reinterpret_cast<long>(int(3));
ilegal)
Si está intentando convertir los bits de su int
a una representación de un double
, es necesario emitir el Dirección no el valor. También debe asegurarse de que los tamaños coinciden:
uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
El compilador rechaza lo que escribió como una tontería porque int
y double
pueden ser objetos de diferentes tamaños. Se podría conseguir el mismo efecto de esta manera, aunque es ciertamente peligroso:
int x = 0;
double y = *reinterpret_cast<double*>(&x);
Esto es potencialmente peligroso ya que si x
y y
son diversos tamaños (digamos int
es de cuatro bytes y double
es de ocho bytes), entonces cuando eliminar la referencia de los ocho bytes de memoria en &x
rellenar y
se accede a cuatro bytes de x
y cuatro bytes de ... lo que venga después en la memoria (posiblemente el inicio de y
, o basura, o algo completamente distinto.)
Si desea convertir un número entero a un doble, use un static_cast
y se llevará a cabo la conversión.
Si desea acceder al patrón de bits de x
, fundido a algún tipo de puntero conveniente (por ejemplo, byte*
) y el acceso hasta sizeof(int) / sizeof(byte)
:
byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
// do something with p[i]
}
reinterpretar fundido le permite reinterpretar un bloque de memoria como un tipo diferente. Esto tiene que ser realizada sobre los punteros o referencias
int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f ); // !!
La otra cosa es que es de hecho un reparto bastante peligroso, no sólo debido a los valores extraños que salen como los resultados, o la aserción anterior no fallan, sino porque si los tipos son de diferentes tamaños, y se reinterpretan desde ' fuente' a '' tipos de destino, cualquier operación en la referencia / puntero reinterpretados accederá bytes sizeof(destination)
. Si sizeof(destination)>sizeof(source)
entonces que va a ir más allá de la memoria de variables reales, lo que podría matar a su aplicación o overwritting otras variables distintas de la fuente o destino:
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
es la mejor opción para los punteros. Por lo que un puntero a un objeto se puede convertir en un "submarino".
MSDN :
El operador reinterpret_cast puede ser utilizado para las conversiones tales como char * a int * o * a One_class Unrelated_class *, que son inherentemente inseguro.
El resultado de una reinterpret_cast No puede con seguridad ser utilizado para cualquier cosa aparte de ser echado de nuevo a su tipo original. Otros usos son, por lo mejor, no portátil.
El enfoque reinterpretar me llevó por un camino extraño, con resultados inconstantes. Al final me encontré mucho mejor a memcpy así!
double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));
casting un int a un doble no requiere un yeso. El compilador de realizar los trabajos de manera implícita.
El reinterpret_cast se utiliza con punteros y referencias, por ejemplo, que emitan un int *
a un double *
.
Eso es interesante. A lo mejor es hacer una conversión implícita de int a flotar antes de intentar el elenco de duplicar. int y flotan tipos tienden a ser del mismo tamaño en bytes (dependiendo de su sistema, por supuesto).