代码之家  ›  专栏  ›  技术社区  ›  Amy

在.NET中反转字节顺序

  •  13
  • Amy  · 技术社区  · 15 年前

    在下面的代码中,为什么x和y的值不同于我直观地认为的值?

    如果字节0-7被写入缓冲区,那么结果的long是否应该具有相同顺序的字节?它就像是以相反的顺序读取长值。

    x    0x0706050403020100    long
    y    0x0706050403020100    long
    z    0x0001020304050607    long
    
    MemoryStream ms = new MemoryStream();
    byte[] buffer = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
    ms.Write(buffer, 0, buffer.Length);
    ms.Flush();
    ms.Position = 0;
    
    BinaryReader reader = new BinaryReader(ms);
    long x = reader.ReadInt64();
    long y = BitConverter.ToInt64(buffer, 0);
    long z = BitConverter.ToInt64(buffer.Reverse<byte>().ToArray<byte>(), 0);
    
    byte[] xbytes = BitConverter.GetBytes(x);
    byte[] ybytes = BitConverter.GetBytes(y);
    byte[] zbytes = BitConverter.GetBytes(z);
    

    (除了.NET,我不知道如何标记这个问题。)


    BitConverter.IsLittleEndian
    

    是假的。如果我的计算机是big endian,为什么会发生这种情况?

    • 这是Windows 7 64位计算机
    • Intel Core2 Quad Q9400 2.66 GHz LGA 775 95W四核处理器型号BX80580Q9400
    • Supermicro MBD-C2SBX+-O LGA 775 Intel X48 ATX Intel主板

    此代码的结果(响应Jason的评论):

    byte[] buffer = new byte[] { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
    long y = BitConverter.ToInt64(buffer, 1);
    Console.WriteLine(BitConverter.IsLittleEndian);
    Console.WriteLine(y);
    

    结果:

    False
    506097522914230528
    
    7 回复  |  直到 15 年前
        1
  •  21
  •   jason    15 年前

    BinaryReader.ReadInt64 设计上是小结尾。从文档中:

    BinaryReader以小endian格式读取此数据类型。

    事实上,我们可以检查 二进制阅读器.readInt64 使用反射镜。

    public virtual long ReadInt64() {
        this.FillBuffer(8);
        uint num = (uint) (((this.m_buffer[0] |
                  (this.m_buffer[1] << 0x08)) |
                  (this.m_buffer[2] << 0x10)) |
                  (this.m_buffer[3] << 0x18));
        uint num2 = (uint) (((this.m_buffer[4] |
                   (this.m_buffer[5] << 0x08)) |
                   (this.m_buffer[6] << 0x10)) |
                   (this.m_buffer[7] << 0x18));
        return (long) ((num2 << 0x20) | num);
    }
    

    表明 二进制阅读器.readInt64 作为独立于底层机器体系结构的小endian进行读取。

    现在, BitConverter.ToInt64 是为了尊重底层机器的结尾。在反射镜中我们可以看到

    public static unsafe long ToInt64(byte[] value, int startIndex) {
        // argument checking elided
        fixed (byte* numRef = &(value[startIndex])) {
            if ((startIndex % 8) == 0) {
                return *(((long*) numRef));
            }
            if (IsLittleEndian) {
                int num = (numRef[0] << 0x00) |
                          (numRef[1] << 0x08) |
                          (numRef[2] << 0x10) |
                          (numRef[3] << 0x18);
                int num2 = (numRef[4] << 0x00) |
                           (numRef[5] << 0x08) |
                           (numRef[6] << 0x10) |
                           (numRef[7] << 0x18);
                return (((long) ((ulong) num)) | (num2 << 0x20));
            }
            int num3 = (numRef[0] << 0x18) |
                       (numRef[1] << 0x10) |
                       (numRef[2] << 0x08) |
                       (numRef[3] << 0x00);
            int num4 = (numRef[4] << 0x18) |
                       (numRef[5] << 0x10) |
                       (numRef[6] << 0x08) |
                       (numRef[7] << 0x00);
            return (((long) ((ulong) num4)) | (num3 << 0x20));
    }
    

    所以我们在这里看到的是如果 startIndex 与从地址开始的八个字节直接转换的零模八一致 numRef . 由于校准问题,本案特别处理。代码行

    return *(((long *) numRef));
    

    直接转换为

        ldloc.0      ;pushes local 0 on stack, this is numRef
        conv.i       ;pop top of stack, convert to native int, push onto stack
        ldind.i8     ;pop address off stack, indirect load from address as long
        ret          ;return to caller, return value is top of stack
    

    所以我们看到在这种情况下,关键是 ldind.i8 指令。cli对于底层机器的终结性是不可知的。它允许JIT编译器处理该问题。在一台小型Endian机器上, LDI.I8 将更高的地址加载到更有效的位中,并加载到big endian机器上 LDI.I8 将高地址加载到低有效字节中。因此,在这种情况下,endianness被正确地处理。

    在另一种情况下,您可以看到对静态属性的显式检查 BitConverter.IsLittleEndian . 在小endian的情况下,缓冲区被解释为小endian(因此内存 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 } 被解释为长 0x0706050403020100 )对于big endian,缓冲区被解释为big endian(因此内存 0x00、0x01、0x02、0x03、0x04、0x05、0x06、0x07_ 被解释为长 0x0001020304050607 )所以,为了 BitConverter 这一切都归结到了下缸机的末端。我注意到你在Windows7x64的英特尔芯片上。英特尔的芯片是小endian。我注意到在Reflector中, 位转换器 定义如下:

    static BitConverter() {
        IsLittleEndian = true;
    }
    

    这在我的Windows Vista x64计算机上。(例如,Xbox 360上的.NET CF可能有所不同。)Windows 7 X64没有任何不同的原因。因此,你确定 Bitconverter.Islittleendian false ?应该是 true 因此你看到的行为是正确的。

        2
  •  5
  •   I. J. Kennedy ShankarSangoli    15 年前

    你在一个 little endian 首先存储整数的最低有效字节的机器。

        3
  •  4
  •   mletterle    15 年前
        4
  •  3
  •   Community CDub    8 年前

    你完全确定Bitconverter.Islittleendian返回的是假的吗?

    如果在使用调试程序的任何方法之前通过Debugger Watch检查它,那么即使它应该返回true,也可能会得到false。

    通过代码读取值以完全确定。 也见 IsLittleEndian field reports false, but it must be Little-Endian?

        5
  •  2
  •   Community CDub    8 年前

    如果您关心字节的尾数,jon skeet编写了一个类,允许您在进行转换时选择尾数顺序。

    C# little endian or big endian?

        6
  •  2
  •   Tim Cooper    10 年前

    只是:

    if (BitConverter.IsLittleEndian == true) Array.Reverse(var);
    
        7
  •  1
  •   Edward Brey    10 年前

    BitConverter 使用它运行的机器的结束键。要确保使用大尾数,请使用 IPAddress.HostToNetworkOrder . 例如:

    IPAddress.HostToNetworkOrder(BitConverter.ToInt64(buffer))