NET中的反转字节顺序 [英] Reversing byte order in .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屋!