int 到 float 的不安全转换,*(float*) (&num) 是做什么的?

In int to float unsafe conversion, what's does *(float*) (&num) do?

我正在写一个流 API,它使用了很多必须处理 BigEndian/LittleEndian 和无符号与有符号转换的东西。我有以下代码,它确实有效。但我想知道它在做什么,可以请一些 ELI5(像我 5 岁一样解释)吗?

int num = ReadInt();
return *(float*) (&num);

 public int ReadInt()
        {
            return 
                (_memoryStream.ReadByte() << 0x18) |
                (_memoryStream.ReadByte() << 0x10) |
                (_memoryStream.ReadByte() << 0x08) |
                _memoryStream.ReadByte();
        }

现在我明白了 ReadInt 的作用,它只是将 LittleEndian 转换为 BigEndian。我似乎无法理解的部分是 *(float*)(&num).

我猜它在做某种记忆转移,但不是 100%。

一步步解释:

  1. & 获取指向 num.
  2. 中包含的对象的指针
  3. (float *) 将此指针转换为浮点指针。
  4. *取消对this指针的引用,即获取该指针处的值。

总的来说,这 returns 字节流中最近的四个字节作为浮点数。不过,您可能会说,这可能不是最好的编码方式。

如果我敢猜的话,这是这样写的,因为 intfloat 之间的直接转换会 preserve the value 而不是 preserve the value正在读入的字节数,这可能不是所需的行为。

&num -- "the address of num",这是一个指向整数的指针。

(float*)&num - "the address of num, converted to a pointer to float."

这是num的内存地址,但编译器会将其内容解释为浮点数,而不是整数。

*(float*)&num - "the address of num, converted to a pointer to float"的内容 - 也就是说,将num占用的内存作为浮点数读出。

因此,如果您要读取字节 0x40、0x10、0x00、0x00, 你会在内存位置得到整数 0x40100000 = 1074790400 'num' 'num' 的地址将被转换为指向浮点数的指针,并且 你会提取其中的内容。常量 0x40100000,在解释时 作为浮点数,是 2.25,这就是你要 return.

假设这是不安全的 C# 代码,ReadInt 通过将每个字节移动 24 位、16 位,将 BigEndian 有序字节流中的 4 个字节加载到 int 的 4 个字节中, 8bits, 和 0(与 C 等语言不同,.Net 中的 int 保证是 4 个字节,无论平台如何。

如您所料,这可能是 'endian-agnostic' 库的一部分,该库使用自己专有的二进制序列化格式。几乎肯定会有一个相应的 SaveInt 方法,它同样会以相应的字节顺序将 4 字节的 int 保存到流中。

别人已经解释过了,即代码:

int num = ReadInt();
return *(float*) (&num);

重新解释假定的 32 位 int 的前 4 个字节(存储在内存中),作为单精度浮点数,存储为 IEEE 754 格式浮点布局 here.使用的技术是将指向 int 地址的指针重新转换为指向 float 的指针,然后对值进行取值。

重要的是,float 也存储在 .Net 中的 4 个字节中(与我们读过的 int 大小相同):

  • 1 位符号
  • 8 位指数
  • 23 位尾数

为什么?从 int 到 float 的直接转换:

int num = ReadInt();
return (float)num;

不会恢复原始的 'float' 序列化为 4​​ 字节,而是保留 4 字节 int 的整数值。

既然你说这段代码来自一个库,那么 ReadInt 很可能也是其他转换中使用的实用方法 - 即其他消耗 4 个字节的类型也可以使用 ReadInt 方法可能会做类似的 post-read 'reinterpretation' 读取的字节。

这种技术显然存在危险,例如如果返回的 int 占用的字节数不足以满足更大的数据类型,例如:

int num = ReadInt();
return *(double*) (&num); // Oops.