74

我知道这reinterpret_cast很危险,我这样做只是为了测试它。我有以下代码:

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

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

从“float”类型到“double”类型的无效转换

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

4

11 回答 11

51

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

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

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

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

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

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

int在任何情况下,这当然在具有不同大小的平台上没有多大意义double(因为在更大的情况下,double您将读取超出所占用的内存x)。

所以,最终这一切都归结为你想要实现的目标。记忆重新诠释?看上面。某种更有意义intdouble转换?如果是这样,reinterpret_cast不会在这里帮助你。

于 2010-02-05T09:30:04.600 回答
47

也许更好的思考方式reinterpret_cast是胭脂操作符,它可以将指向苹果的指针“转换”指向潜艇的指针。

通过将 y 分配给强制转换返回的值x,您实际上并没有强制转换 value ,而是在转换它。也就是说,y不指向x并假装它指向一个浮点数。Conversion 构造一个新的 type 值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。

于 2010-02-05T06:24:29.080 回答
12

reinterpret_cast 不是一般演员表。根据 C++03 规范第 5.2.10.1 节:

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

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

于 2010-02-05T06:32:46.180 回答
12

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

uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
于 2010-02-05T06:18:22.657 回答
6

编译器拒绝你写的废话,因为int并且double可能是不同大小的对象。您可以通过这种方式达到相同的效果,尽管这肯定很危险:

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

这有潜在的危险,因为如果xy是不同的大小(假设int是四个字节和double八个字节),那么当您取消引用八个字节的内存&x来填充时,y您将访问四个字节x和四个字节......无论接下来发生什么在内存中(可能是y,或垃圾,或完全其他的东西的开始。)

如果要将整数转换为双精度,请使用 astatic_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]
}
于 2010-02-05T06:24:05.440 回答
4

Reinterpret cast 允许您将内存块重新解释为不同的类型。这必须在指针或引用上执行:

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

另一件事是它实际上是一个非常危险的转换,不仅因为奇怪的值作为结果出现,或者上面的断言没有失败,还因为如果类型的大小不同,并且你从“源”重新解释为'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.
assert( t.y != 20 );
于 2010-02-05T09:11:59.230 回答
2

重新解释的方法让我走上了一条奇怪的道路,结果不一致。最后我发现像这样 memcpy 更好!

double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof dest);
于 2019-01-25T06:58:28.927 回答
0

reinterpret_cast最好用于指针。所以指向一个对象的指针可以变成“潜艇”。

来自msdn

reinterpret_cast 运算符可用于 char* 到 int* 或 One_class* 到 Unrelated_class* 等转换,这些转换本质上是不安全的。

reinterpret_cast 的结果除了被转换回其原始类型外,不能安全地用于任何其他事情。其他用途充其量是不可移植的。

于 2010-02-05T06:12:04.973 回答
0

那很有意思。也许它在尝试将强制转换加倍之前进行了从 int 到 float 的隐式转换。int 和 float 类型的字节大小往往相同(当然取决于您的系统)。

于 2010-02-05T06:19:15.700 回答
-1

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

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

于 2010-02-05T06:13:15.367 回答
-1

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

#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);
}
于 2020-07-12T14:04:55.340 回答