为什么这个reinterpret_cast没有编译?

时间:2010-02-05 06:10:04

标签: c++ casting reinterpret-cast

我理解reinterpret_cast是危险的,我只是这样做来测试它。我有以下代码:

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

当我尝试编译程序时,它给出了一个错误说

  

从'float'类型转换为'double

类型无效

发生了什么事?我认为reinterpret_cast是你可以用来将苹果转换为潜艇的流氓演员,为什么这个简单的演员不会编译?

11 个答案:

答案 0 :(得分:41)

通过将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是一个浮点数,那么你确实想要通过这样做来投射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.

答案 1 :(得分:39)

在C ++中reinterpret_cast只能执行一组特定的转换,在语言规范中明确列出。简而言之,reinterpret_cast只能执行指针到指针的转换和引用到引用的转换(加上指向整数的指针和整数到指针的转换)。这与演员名称中表达的意图一致:它旨在用于指针/引用重新解释。

你要做的不是重新解释。如果您想将int重新解释为double,则必须将其转换为引用类型

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

虽然等效的基于指针的重新解释可能更明确

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

但请注意,虽然reinterpret_cast可以转换引用/指针类型,但实际尝试通过结果引用/指针读取数据会产生未定义的行为。

在任何情况下,这对于intdouble不同大小的平台来说都没有多大意义(因为如果double更大,你会读到超出x)占用的内存。

所以,最终它归结为你想要实现的目标。记忆重新解释?往上看。某种更有意义的intdouble转换?如果是这样,那么reinterpret_cast将无法帮助您。

答案 2 :(得分:11)

reinterpret_cast不是一般演员。根据C ++ 03规范部分5.2.10.1:

  

下面列出了可以使用reinterpret_cast显式执行的转换。使用reinterpret_cast不能显式执行其他转换。

并没有列出任何描述积分和浮点类型之间(或整数类型之间的转换,甚至这是非法的reinterpret_cast<long>(int(3));

答案 3 :(得分:10)

如果您尝试将int的位转换为double的表示,则需要将地址转换为值。您还必须确保尺寸匹配:

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

答案 4 :(得分:3)

编译器拒绝您写的废话,因为intdouble可能是不同大小的对象。你可以用这种方式达到同样的效果,虽然它肯定很危险:

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

这有潜在危险,因为如果xy的大小不同(假设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]
}

答案 5 :(得分:3)

重新解释强制转换允许您将内存块重新解释为不同的类型。这必须在指针或引用

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

另一件事是它实际上是一个非常危险的演员,不仅是由于奇怪的值作为结果出现,或者上面的断言没有失败,而是因为如果类型有不同的大小,你重新解释' source'到'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 );

答案 6 :(得分:1)

reinterpret_cast最适合用于指针。因此,指向一个对象的指针可以变成“潜艇”。

来自msdn

  

reinterpret_cast运算符可以是   用于转换,如char * to   int *,或One_class * to   Unrelated_class *,本质上是   不安全的。

     

reinterpret_cast的结果   不能安全地用于任何事情   除了被抛弃之外   原始类型。其他用途是,在   最好,不便携。

答案 7 :(得分:0)

将int转换为double不需要强制转换。编译器将隐式执行赋值。

reinterpret_cast与指针和引用一起使用,例如,将int *转换为double *

答案 8 :(得分:0)

这很有趣。也许它在尝试将转换加倍之前进行从int到float的隐式转换。 int和float类型的大小相同(以字节为单位)(具体取决于您的系统)。

答案 9 :(得分:0)

重新解释方法使我走上了一条不稳定的道路,结果一直不稳定。最后,我发现像这样进行memcpy更好!

double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));

答案 10 :(得分:0)

使用联合。这是在整数和浮点类型之间进行内存映射的最不易出错的方法。重新解释指针将导致别名警告。

#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);
}