为什么此reinterpret_cast无法编译?


70

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

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

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

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

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


我从来没有在C ++中尝试过,所以我只是在猜测。如果您投下浮标,这行得通吗?难道两种类型的位长不一样吗?
克里斯·皮特曼

reinterpret_cast<double>(x)“您希望这个表情做什么?
curiousguy 2012年

3
int是32位。double是64位。那可能就是问题。你可以检查一下吗?
user4951 2012年

您确定编译器会抱怨float吗?float您的代码段中没有。
罗斯兰

Answers:


47

也许更好的思路reinterpret_cast是胭脂运算符,它可以将“苹果”的指针“转换”为潜艇的指针

通过将y赋值给强制类型转换返回的值x,您实际上并没有强制转换该值,而是在转换它。也就是说,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是一个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。


(float)x将返回42或一些不相关的double的二进制表示。我认为这个人意味着重新诠释,这就是他想要的。
user4951 2012年

(float)x将执行转换,而不是强制转换。
John Dibling 2012年

2
您的以下代码严格违反别名,因此具有未定义的行为。
Maciej Piechotka

当我在低级代码中执行此类操作时,通常是显示没有应用任何转换的位(可打印十六进制除外)。例如,与微控制器交换(反序列化)数据结构时。想想“调试器”。
Technophile

50

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

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

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

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

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

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

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

因此,最后一切都归结为您要实现的目标。内存重新解释?往上看。某种更有意义intdouble转换吗?如果是这样,reinterpret_cast将不会对您有帮助。


1
reinterpret_cast can only perform pointer-to-pointer conversions and reference-to-reference conversions (plus pointer-to-integer and integer-to-pointer conversions)这使问题变得平坦,可以被当作答案。
RaGa__M

12

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

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

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


11

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

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

4

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

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

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

如果要将整数转换为双精度数,请使用a 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]
}

2
这不是编译器拒绝它的原因,但是有关类型大小的讨论很有价值。
DavidRodríguez-dribeas 2010年

3

通过重新解释类型转换,您可以将内存块重新解释为其他类型。这必须在指针或引用上执行:

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

另一件事是,这实际上是一个非常危险的强制转换,这不仅是因为结果中出现了奇怪的值,还是因为上面的断言没有失败,还因为如果类型的大小不同,并且您将“源”重新解释为“目标”类型,对重新解释的引用/指针的任何操作都将访问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 );

1

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

来自msdn

reinterpret_cast运算符可用于从char *到int *或从One_class *到Unrelated_class *的转换,这些转换本来就不安全。

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


0

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

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


0

那很有意思。可能是在尝试将类型转换为两倍之前,进行了从int到float的隐式转换。int和float类型的字节大小通常相同(当然,这取决于您的系统)。


0

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

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

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

不,以这种方式使用并集是特定于实现的,因此不是最不容易出错的方式(请参阅C和C ++中的并集目的)例如:如果i是32位且f是64位,则i对应于高32位还是低32位的位f
fcdt

如果大小不匹配,则重新解释指针会导致更大的问题,但是您的链接非常好。我承认,工会为此目的没有足够的基础,并且可能存在可移植性问题,但这是任务中固有的。
MrRickDean
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.