在下面的代码中,为什么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
是假的。如果我的电脑是大端,为什么会这样?
此代码的结果(回应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
答案 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.IsLittleEndian
是false
吗?它应该是true
,因此您看到的行为是正确的。
答案 1 :(得分:5)
你在little endian机器上,其中整数首先存储在最低有效字节。
答案 2 :(得分:4)
BinaryReader假定Little Endian顺序:http://msdn.microsoft.com/en-us/library/system.io.binaryreader.readint64.aspx
答案 3 :(得分:3)
您是否完全确定BitConverter.IsLittleEndian返回false?
如果在使用任何方法之前通过调试器监视检查它,即使它应该返回true,也可能会出错。
通过代码读取值是完全确定的。 另请参阅IsLittleEndian field reports false, but it must be Little-Endian?
答案 4 :(得分:2)
如果您关心字节的字节序,Jon Skeet写了一个类,允许您在进行转换时选择字节顺序。
答案 5 :(得分:2)
只是:
if (BitConverter.IsLittleEndian == true) Array.Reverse(var);
答案 6 :(得分:1)
BitConverter
使用正在运行的机器的字节序。要确保大端编号,请使用IPAddress.HostToNetworkOrder
。例如:
IPAddress.HostToNetworkOrder(BitConverter.ToInt64(buffer))