在下面的代码中,为什么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);
Run Code Online (Sandbox Code Playgroud)
(除了.NET之外,我不知道要标记这个问题的内容.)
BitConverter.IsLittleEndian
Run Code Online (Sandbox Code Playgroud)
是假的.如果我的电脑是大端,为什么会这样?
这段代码的结果(回应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);
Run Code Online (Sandbox Code Playgroud)
结果:
False
506097522914230528
Run Code Online (Sandbox Code Playgroud)
jas*_*son 22
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);
}
Run Code Online (Sandbox Code Playgroud)
显示该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));
}
Run Code Online (Sandbox Code Playgroud)
所以我们在这里看到的是,如果startIndex对于零模8是一致的,那么从地址开始的8个字节完成直接转换numRef.由于对齐问题,特殊处理此案例.代码行
return *(((long *) numRef));
Run Code Online (Sandbox Code Playgroud)
直接翻译成
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
Run Code Online (Sandbox Code Playgroud)
所以我们看到在这种情况下,关键是ldind.i8指令.CLI与底层机器的字节顺序无关.它让JIT编译器处理该问题.在小端机器上,ldind.i8将更高的地址加载到更重要的位和大端机器上ldind.i8上将更高的地址加载到不太重要的字节.因此,在这种情况下,正确处理字节顺序.
在另一种情况下,您可以看到显式检查静态属性BitConverter.IsLittleEndian.在little endian的情况下,缓冲区被解释为little endian(因此内存{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }被解释为long 0x0706050403020100),而在big endian的情况下,缓冲区被解释为big endian(因此内存{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }被解释为long 0x0001020304050607).因此,对于BitConverter这一切都归结为底层机器的字节序.我注意到你使用的是Windows 7 x64上的英特尔芯片.英特尔芯片是小端.我注意到在Reflector中,静态构造函数for BitConverter定义如下:
static BitConverter() {
IsLittleEndian = true;
}
Run Code Online (Sandbox Code Playgroud)
这是在我的Windows Vista x64机器上.(例如,XBox 360上的.NET CF可能会有所不同.)Windows 7 x64没有任何理由不同.因此,你确定BitConverter.IsLittleEndian是false吗?它应该是true,因此你看到的行为是正确的.