BitConverter.ToInt32是如何工作的?

15

这里有一个方法 -

using System;

class Program
{
    static void Main(string[] args)
    {
        //
        // Create an array of four bytes.
        // ... Then convert it into an integer and unsigned integer.
        //
        byte[] array = new byte[4];
        array[0] = 1; // Lowest
        array[1] = 64;
        array[2] = 0;
        array[3] = 0; // Sign bit
        //
        // Use BitConverter to convert the bytes to an int and a uint.
        // ... The int and uint can have different values if the sign bit differs.
        //
        int result1 = BitConverter.ToInt32(array, 0); // Start at first index
        uint result2 = BitConverter.ToUInt32(array, 0); // First index
        Console.WriteLine(result1);
        Console.WriteLine(result2);
        Console.ReadLine();
    }
}

输出

16385 16385

我只是想知道这是怎么发生的?

6个回答

16

BitConverter.ToInt32的文档实际上有一些很好的例子。假设BitConverter.IsLittleEndian返回true,array[0]是最不重要的字节,就像你展示的一样...尽管array[3]不仅仅是符号位,它是最重要的字节,其中包括符号位(作为第7位),但其余的位用于表示大小。

因此,在您的情况下,最不重要的字节是1,下一个字节是64-因此结果是:

( 1 * (1 << 0) ) +    // Bottom 8 bits
(64 * (1 << 8) ) +    // Next 8 bits, i.e. multiply by 256
( 0 * (1 << 16)) +    // Next 8 bits, i.e. multiply by 65,536
( 0 * (1 << 24))      // Top 7 bits and sign bit, multiply by 16,777,216

这个数字是16385。如果符号位被设置,你需要分别考虑两种情况,但在这种情况下很简单。


3
尝试抵制将“+”替换为“|”的冲动 ;p - Marc Gravell
1
@Marc:是的,我也不确定……我觉得如果楼主在询问这个问题,他们可能更喜欢使用+。 - Jon Skeet

4

它的转换方式就像一个256进制数一样。所以在您的情况下:1+64*256 = 16385


3
在查看 .Net 4.0 Framework 的 参考源 时,BitConverter 确实像 Jon's answer 所说的那样工作,但它使用指针 (unsafe 代码) 处理数组。
然而,如果第二个参数(即 startindex) 可以被 4 整除 (正如您的示例所示),框架会采取捷径。它将一个 byte 指针指向 value[startindex],并将其转换为一个 int 指针,然后对其进行解引用。这个技巧不管 IsLittleEndian 是否为真都适用。
从高层次来看,这基本上意味着代码指向字节数组中的4个字节,并明确声明“那里的内存块是一个int!”(然后返回其副本)。当您考虑到底层实际上一个int只是一块内存时,这就很有道理了。
下面是框架ToUint32方法的源代码:
return (uint)ToInt32(value, startIndex);

这些是我感兴趣的位。谢谢! - Sebastian Graf

0
数组[0] = 1; // 最低位 // 0x01
数组[1] = 64; // 0x40
数组[2] = 0; // 0x00
数组[3] = 0; // 符号位 0x00

如果将每个十六进制值组合起来,得到0x00004001

MSDN文档解释了一切。


0

-1
对于那些在处理 Little Endian 和 Big Endian 时遇到问题的人,我使用以下包装函数来解决它。
    public static Int16 ToInt16(byte[] data, int offset)
    {
        if (BitConverter.IsLittleEndian)
        {
            return BitConverter.ToInt16(BitConverter.IsLittleEndian ? data.Skip(offset).Take(2).Reverse().ToArray() : data, 0);
        }
        return BitConverter.ToInt16(data, offset);
    }

    public static Int32 ToInt32(byte[] data, int offset)
    {
        if (BitConverter.IsLittleEndian)
        {
            return BitConverter.ToInt32(BitConverter.IsLittleEndian ? data.Skip(offset).Take(4).Reverse().ToArray() : data, 0);
        }
        return BitConverter.ToInt32(data, offset);
    }

    public static Int64 ToInt64(byte[] data, int offset)
    {
        if (BitConverter.IsLittleEndian)
        {
            return BitConverter.ToInt64(BitConverter.IsLittleEndian ? data.Skip(offset).Take(8).Reverse().ToArray() : data, 0);
        }
        return BitConverter.ToInt64(data, offset);
    }

BitConverter已经考虑了架构的字节序。如果您确实想提供一个独立于当前架构的字节序参数的ToIntxx函数,我的建议是将BitConverter.IsLittleEndian替换为BitConverter.IsLittleEndian == isLittleEndian,其中isLittleEndian是传递给包装器函数的参数。此外,我发现你在if语句和三元运算符中都检查了BitConverter.IsLittleEndian,这样做很不好。要么使用if语句,要么使用三元运算符,但不要同时使用两者。 - Brian
如果你的担忧是BitConverter.IsLittleEndian中某种竞态条件的问题,那么你的担忧是不必要的。首先,你的解决方案并没有解决这样的竞态条件(虽然它确实缩小了范围)。其次,BitConverter.IsLittleEndian指示架构字节序。 IsLittleEndian的值在框架的编译时设置。 - Brian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接