16

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

7 回答 7

23

BinaryReader.ReadInt64是小端设计的。从文档中:

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

实际上,我们可以查看BinaryReader.ReadInt64使用 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是独立于底层机器架构的小端。

现在,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与零模八一致,则直接转换是从从 address 开始的八个字节完成的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 编译器处理这个问题。在 little-endian 机器上,ldind.i8会将较高的地址加载到较高的有效位中,而在 big-endian 的机器上,ldind.i8会将较高的地址加载到较低的有效字节中。因此,在这种情况下,字节序得到了妥善处理。

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

static BitConverter() {
    IsLittleEndian = true;
}

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

于 2010-01-07T16:05:04.747 回答
6

你在一个小端机器上,其中整数首先存储最低有效字节。

于 2010-01-05T00:36:41.913 回答
4

BinaryReader 采用 Little Endian 顺序:http: //msdn.microsoft.com/en-us/library/system.io.binaryreader.readint64.aspx

于 2010-01-05T00:48:45.570 回答
3

您完全确定 BitConverter.IsLittleEndian 返回错误吗?

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

通过代码读出值是完全确定的。另请参阅IsLittleEndian 字段报告错误,但它必须是 Little-Endian?

于 2011-05-27T18:54:41.980 回答
2

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

看到C# 小端还是大端?

于 2010-01-05T00:51:36.673 回答
2

只是:

if (BitConverter.IsLittleEndian == true) Array.Reverse(var);
于 2013-05-16T15:34:18.357 回答
1

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

IPAddress.HostToNetworkOrder(BitConverter.ToInt64(buffer))
于 2015-04-30T09:43:16.140 回答