在.NET中反转字节顺序

时间:2010-01-05 00:32:36

标签: .net endianness

在下面的代码中,为什么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

是假的。如果我的电脑是大端,为什么会这样?

  • 这是一台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 个答案:

答案 0 :(得分:21)

BinaryReader.ReadInt64是设计上的小端。来自文档:

  

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

事实上,我们可以使用Reflector检查BinaryReader.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);
}

显示BinaryReader.ReadInt64读取为小端,独立于底层机器架构。

现在,BitConverter.ToInt64假设尊重底层机器的字节顺序。在Reflector我们可以看到

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与零模8一致,则从地址numRef开始的8个字节进行直接强制转换。由于对齐问题,特殊处理此案例。代码行

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会将更高的地址加载到不太重要的字节中。因此,在这种情况下,正确处理字节顺序。

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

static BitConverter() {
    IsLittleEndian = true;
}

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

答案 1 :(得分:5)

你在little endian机器上,其中整数首先存储在最低有效字节。

答案 2 :(得分:4)

答案 3 :(得分:3)

您是否完全确定BitConverter.IsLittleEndian返回false?

如果在使用任何方法之前通过调试器监视检查它,即使它应该返回true,也可能会出错。

通过代码读取值是完全确定的。 另请参阅IsLittleEndian field reports false, but it must be Little-Endian?

答案 4 :(得分:2)

如果您关心字节的字节序,Jon Skeet写了一个类,允许您在进行转换时选择字节顺序。

请参阅C# little endian or big endian?

答案 5 :(得分:2)

只是:

if (BitConverter.IsLittleEndian == true) Array.Reverse(var);

答案 6 :(得分:1)

BitConverter使用正在运行的机器的字节序。要确保大端编号,请使用IPAddress.HostToNetworkOrder。例如:

IPAddress.HostToNetworkOrder(BitConverter.ToInt64(buffer))