Почему этот reinterpret_cast не компилируется?
Я понимаю, что reinterpret_cast
опасен, я просто делаю это, чтобы проверить это. У меня есть следующий код:
int x = 0;
double y = reinterpret_cast<double>(x);
Когда я пытаюсь скомпилировать программу, выдает ошибку
недопустимое приведение типа 'float' к типу 'double
Что происходит? Я думал, что reinterpret_cast
- это мошенническое приведение, которое вы можете использовать для преобразования яблок в подводные лодки, почему этот простой состав не скомпилируется?
Ответы
Ответ 1
Назначив y значению, возвращаемому приложением, вы действительно не набрасываете значение x
, вы его конвертируете. То есть, y
не указывает на x
и делает вид, что указывает на поплавок. Conversion создает новое значение типа float
и присваивает ему значение от x
. Существует несколько способов сделать это преобразование в С++, среди которых:
int main()
{
int x = 42;
float f = static_cast<float>(x);
float f2 = (float)x;
float f3 = float(x);
float f4 = x;
return 0;
}
Единственное реальное различие, являющееся последним (неявное преобразование), будет генерировать диагностику компилятора на более высоких уровнях предупреждения. Но все они выполняют функционально то же самое - и во многих случаях фактически то же самое, что и в одном машинном коде.
Теперь, если вы действительно хотите сделать вид, что x
- это float, тогда вы действительно хотите использовать x
, выполнив следующее:
#include <iostream>
using namespace std;
int main()
{
int x = 42;
float* pf = reinterpret_cast<float*>(&x);
(*pf)++;
cout << *pf;
return 0;
}
Вы можете видеть, насколько это опасно. Фактически, вывод, когда я запускаю это на моей машине, 1
, который явно не 42 + 1.
Ответ 2
В С++ reinterpret_cast
может выполняться только определенный набор преобразований, явно указанный в спецификации языка. Короче говоря, reinterpret_cast
может выполнять только преобразования указателя на указатель и преобразования ссылки на ссылку (плюс преобразования указателя на целое и целочисленные в указатели). Это согласуется с намерением, выраженным в самом имени приведения: оно предназначено для использования для интерпретации указателя/ссылки.
То, что вы пытаетесь сделать, это не переинтерпретация. Если вы хотите переосмыслить int
как double
, вам придется преобразовать его в ссылочный тип
double y = reinterpret_cast<double&>(x);
хотя эквивалентная реинтерпретация на основе указателей, вероятно, более явная
double y = *reinterpret_cast<double*>(&x); // same as above
Обратите внимание, что хотя reinterpret_cast
может преобразовать типы ссылок/указателей, фактическая попытка считывания данных через результирующий указатель/указатель вызывает поведение undefined.
И в любом случае это, конечно, не может иметь особого смысла на платформе с int
и double
разного размера (поскольку в случае большего double
вы будете читать за пределами памяти, занятой x
).
Итак, в конце концов, все сводится к тому, чего вы пытались достичь. Реинтерпретация памяти? См. Выше. Какое-то более значимое преобразование int
to double
? Если это так, reinterpret_cast
вам не поможет.
Ответ 3
reinterpret_cast не является общепринятым. В соответствии с разделом 5.2.10.1 раздела С++ 03:
Ниже перечислены преобразования, которые могут быть выполнены явно с использованием reinterpret_cast. Никакое другое преобразование не может быть выполнено явно с использованием reinterpret_cast.
И нет ничего перечисленного, описывающего преобразование между целыми и плавающими типами (или между целыми типами, даже это незаконно reinterpret_cast<long>(int(3));
)
Ответ 4
Если вы пытаетесь преобразовать биты вашего int
в представление double
, вам нужно указать адрес, а не значение. Вы также должны убедиться, что размеры соответствуют:
uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
Ответ 5
Компилятор отвергает то, что вы написали как бессмыслицу, потому что int
и double
могут быть объектами разных размеров. Таким образом, вы можете добиться такого же эффекта, хотя это, безусловно, опасно:
int x = 0;
double y = *reinterpret_cast<double*>(&x);
Это потенциально опасно, потому что если x
и y
- разные размеры (скажем, int
- четыре байта, а double
- восемь байт), то при разыменовании восьми байтов памяти в &x
заполните y
, вы получите четыре байта x
и четыре байта... что будет дальше в памяти (возможно, начало y
, или мусор, или что-то еще полностью.)
Если вы хотите преобразовать целое число в double, используйте static_cast
и он выполнит преобразование.
Если вы хотите получить доступ к битовому образцу x
, добавьте к некоторому удобному типу указателя (скажем, byte*
) и получите доступ к sizeof(int) / sizeof(byte)
:
byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
// do something with p[i]
}
Ответ 6
Reinterpret cast позволяет вам переинтерпретировать блок памяти как другой тип. Это должно выполняться по указателям или ссылкам:
int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f ); // !!
Другое дело, что на самом деле это довольно опасный состав, причем не только из-за странных значений, которые выходят в качестве результатов, либо из-за несовместимости утверждений выше, а потому, что, если типы имеют разные размеры, и вы переосмысливаете из " source 'to' destination ', любая операция над реинтерпретированной ссылкой/указателем будет иметь доступ к sizeof(destination)
байтам. Если sizeof(destination)>sizeof(source)
, то это будет выходить за пределы реальной памяти переменных, потенциально убивая ваше приложение или перезаписывая другие переменные, отличные от источника или назначения:
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 );
Ответ 7
reinterpret_cast
лучше всего использовать для указателей. Таким образом, указатель на один объект можно превратить в "подводную лодку".
От msdn:
Оператор reinterpret_cast может быть используется для преобразований, таких как char * to int * или One_class * Unrelated_class *, которые по своей сути небезопасный.
Результат reinterpret_cast нельзя безопасно использовать для чего-либо за исключением того, оригинальный type. Другие виды использования: лучший, непереносимый.
Ответ 8
Приведение int в double не требует приведения. Компилятор выполнит назначение неявно.
Реинтерпрет_каст используется с указателями и ссылками, например, отбрасывая int *
в double *
.
Ответ 9
Это интересно. Может быть, он делает неявное преобразование из int в float, прежде чем он попытается выполнить двойное нажатие. типы int и float имеют одинаковый размер в байтах (в зависимости от вашей системы).
Ответ 10
Переосмысленный подход привел меня на странный путь с непоследовательными результатами. В конце концов, я обнаружил, что гораздо лучше использовать memcpy!
double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));