为什么不这样的reinterpret_cast编译?
-
18-09-2019 - |
题
据我所知,reinterpret_cast
是危险的,我只是在做这个测试吧。我有以下代码:
int x = 0;
double y = reinterpret_cast<double>(x);
当我试图编译程序,它给了我一个错误说
这类型“浮动”无效的转换为类型“双
这是怎么回事?我以为reinterpret_cast
是流氓投,你可以用它来苹果转换为潜艇,为什么不会这样简单的投编译?
解决方案
通过指定y以由你没有真正精铸人的价值x
剧组返回的值,你将它转换。也就是说,y
并不指向x
,假装它指向一个浮动。转化构建体类型float
的一个新值,并分配它从x
值。有几种方法可以做到在C ++这种转换,其中:
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
其他提示
在C ++ reinterpret_cast
只能执行一组特定的转换,明确地在语言规范中列出。总之,reinterpret_cast
只能执行指针到指针转换和参考到引用转换(加指针到整数和整数到指针转换)。这与在铸造的非常名称所表示的意图一致的:它旨在用于指针/引用重新解释
你所试图做的是不是重新解释。如果你想重新诠释的int
作为double
你必须将其转换为引用类型
double y = reinterpret_cast<double&>(x);
虽然等效基于指针的重新解释可能是更明确的
double y = *reinterpret_cast<double*>(&x); // same as above
请注意虽然,而reinterpret_cast
可以转换基准/指针类型,实际尝试通过将得到的参考读出的数据/指针产生不确定行为。
在任何情况下这一点,当然,不能与int
和不同大小的double
平台上多大意义(因为在较大double
的情况下,你会读超出x
占用的内存)。
那么,到底这一切都归结到是你所想要的目的。内存重新解释?往上看。某种更有意义int
来double
转换?如果是这样,reinterpret_cast
不会帮助你在这里。
的reinterpret_cast不是一般的铸造。根据C ++ 03规格部5.2.10.1:
这可以显式使用的reinterpret_cast进行换算在下面列出。可显式使用的reinterpret_cast不执行其他的转换。
和没有列出任何描述积分和浮点类型之间进行转换(或整数类型之间,甚至这是非法的reinterpret_cast<long>(int(3));
)
如果你想你的int
的位转换为double
的表示,你需要转换的地址的不是值。你还必须确保大小匹配:
uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
在编译器会拒绝你所写的无稽之谈,因为int
和double
可以是具有不同尺寸的对象。你可以达到同样的效果这样,虽然它肯定是危险的:
int x = 0;
double y = *reinterpret_cast<double*>(&x);
这是潜在的危险,因为如果x
和y
是不同势的大小(假设int
是四个字节,并将double
是八个字节),那么当你提领的八个字节的&x
填补y
内存,您将访问四个字节x
的和四个字节的...在存储器下一任何到来时(y
的可能的开始,或垃圾,或别的东西完全。)
如果你想一个整数转换为双,使用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]
}
重释铸造允许你重新解释的存储器块为不同类型。这必须在指针的或引用执行:
int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f ); // !!
另一件事是,它实际上是一个相当危险的演员,这不仅是因为出来的结果,或断言上面没有失败,而是因为如果类型是不同的尺寸,并从重新解读“怪值源”到‘目的地’的类型,在重新解释参考/指针的任何操作将访问sizeof(destination)
字节。如果sizeof(destination)>sizeof(source)
然后,将步骤超出了实际可变存储器,有可能杀死应用程序或overwritting比源或目的地等其它变量:
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
最好用于指针。因此,一个指针到一个对象可以变成一个“潜艇”。
从 MSDN :
在reinterpret_cast运算符可以是 用于转化如char *到 INT *,或One_class *到 Unrelated_class *,它本质上是 不安全的。
一个的reinterpret_cast的结果 不能安全地用于任何 不是被强制转换回其他公司 原始类型。其他用途的,在 最好,不可移植的。
在重新解释的做法使我打倒见异思迁结果一个奇怪的路径。最后,我发现它要好得多memcpy的这个样子!
double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));
铸造一个int到双不需要铸造。编译器将隐式地执行分配。
在的reinterpret_cast使用具有指针和引用,例如,铸造int *
到double *
。
这是有趣的。也许它做的隐式转换从int浮动它试图投翻番了。 int和float类型往往是在字节相同的尺寸(取决于你当然系统)。