NET中的反转字节顺序 [英] Reversing byte order in .NET

查看:92
本文介绍了NET中的反转字节顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,为什么X和Y的值与我直觉的值不同?

In the code below, why do X and Y take on different values than what I would think intuitively?

如果将字节0-7写入缓冲区,结果长整形的字节数不应该顺序相同吗?

If the bytes 0-7 are written to the buffer, shouldn't the resulting long have bytes in the same order? It's like it's reading the long values in reverse order.

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之外,我不知道该用什么标记)

(I don't know what to tag this question, beyond just .NET.)

BitConverter.IsLittleEndian

为假。如果我的计算机使用大字节序,为什么会发生这种情况?

is false. If my computer is big endian, why does this happen?


  • 这是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的评论):

The results of this code (in response to Jason's comment):

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


推荐答案

BinaryReader.ReadInt64 在设计上有点小尾数。从文档中:

BinaryReader.ReadInt64 is little endian by design. From the documentation:


BinaryReader读取的数据类型为Little-endian格式。

BinaryReader reads this data type in little-endian format.

实际上,我们可以使用Reflector检查 BinaryReader.ReadInt64 的来源。

In fact, we can inspect the source for BinaryReader.ReadInt64 using Reflector.

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

显示 BinaryReader.ReadInt64 读取与基础计算机体系结构无关的小字节序。

Showing that BinaryReader.ReadInt64 reads as little endian independent of the underlying machine architecture.

现在, BitConverter.ToInt64 应该尊重您底层计算机的字节序。在Reflector中,我们可以看到

Now, BitConverter.ToInt64 is suppose to respect the endianness of your underlying machine. In Reflector we can see

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 开始的八个字节进行直接转换。由于对齐问题,因此需要专门处理此案例。代码行

So what we see here is that if startIndex is congruent to zero modulo eight that a direct cast is done from eight bytes starting at address numRef. This case is handled specially because of alignment issues. The line of code

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编译器可以处理该问题。在小字节序的计算机上, ldind.i8 会将较高的地址加载到更重要的位中,而在大字节序的计算机上, ldind.i8 会将更高的地址加载到不太重要的字节中。因此,在这种情况下,字节序处理正确。

So we see that in this case the key is the ldind.i8 instruction. The CLI is agnostic about the endianness of the underlying machine. It lets the JIT compiler handle that issue. On a little-endian machine, ldind.i8 will load higher addresses into more significant bits and on a big-endian machine ldind.i8 will load higher addresses into less significant bytes. Therefore, in this case, endianness is handled properly.

在另一种情况下,您可以看到对静态属性进行了显式检查。 BitConverter.IsLittleEndian 。对于小字节序,缓冲区被解释为小字节序(因此,内存 {0x00、0x01、0x02、0x03、0x04、0x05、0x06、0x07} 被解释为长 0x0706050403020100 ),如果使用大字节序,则缓冲区被解释为大字节序(因此,内存 {0x00、0x01、0x02、0x03、0x04 ,0x05、0x06、0x07} 解释为长 0x0001020304050050 )。因此,对于 BitConverter 来说,这全都归结为底层代码机器的字节序。我注意到您使用的是Windows 7 x64的Intel芯片。英特尔芯片是小端。我注意到在Reflector中, BitConverter 的静态构造函数定义如下:

In the other case, you can see that there is an explicit check of the static property BitConverter.IsLittleEndian. In the case of little endian the buffer is interpreted as little endian (so that memory { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 } is interpreted as the long 0x0706050403020100) and in case of big endian the buffer is interpreted as big endian (so that memory { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 } is interpreted as the long 0x0001020304050607). So, for BitConverter it all comes down to the endianness of the underyling machine. I note that you're on an Intel chip on Windows 7 x64. Intel chips are little endian. I note that in Reflector, the static constructor for BitConverter is defined as the following:

static BitConverter() {
    IsLittleEndian = true;
}

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

This is on my Windows Vista x64 machine. (It could differ on, say, .NET CF on an XBox 360.) There is no reason for Windows 7 x64 to be any different. Consequently, are you sure that BitConverter.IsLittleEndian is false? It should be true and therefore the behavior that you are seeing is correct.

这篇关于NET中的反转字节顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆