我最近一直在研究BitConverter是如何工作的,并且从阅读其他SO问题开始,我已经读到它需要一条“快捷方式”。当起始索引可以被转换为类型的大小整除时,它可以将索引中的字节转换为指向转换为和取消引用它的类型的指针。
以ToInt16的来源为例:
public static unsafe short ToInt16(byte[] value, int startIndex) {
if( value == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
}
if ((uint) startIndex >= value.Length) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
}
if (startIndex > value.Length -2) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
Contract.EndContractBlock();
fixed( byte * pbyte = &value[startIndex]) {
if( startIndex % 2 == 0) { // data is aligned
return *((short *) pbyte);
}
else {
if( IsLittleEndian) {
return (short)((*pbyte) | (*(pbyte + 1) << 8)) ;
}
else {
return (short)((*pbyte << 8) | (*(pbyte + 1)));
}
}
}
}
我的问题是,无论机器的字节顺序如何,为什么它都能正常工作?为什么在数据未对齐时它不会使用相同的机制?
澄清的一个例子:
我在buffer
中有一些字节,我知道它们采用Big endian格式,我想从数组中读取一个short值,比如说索引5.我也假设我的机器,因为它是Windows ,使用小端。
我会像这样使用BitConverter,将字节顺序切换为little endian:
BitConverter.ToInt16(new byte[] { buffer[6], buffer[5] })
假设代码采用了快捷方式,它可以按我的意愿执行:只需按照提供的顺序转换字节并返回值。但是,如果它没有快捷代码,那么它是否会再次反转字节顺序并给我错误的值?或者如果我改为:
BitConverter.ToInt16(new byte[] { 0, buffer[6], buffer[5] }, 1)
它不会给我错误的值,因为索引不能被2整除吗?
另一种情况:
假设我有一个字节数组,其中包含一个我希望以小端格式提取的短文,但是从奇数偏移开始。由于BitConverter.IsLittleEndian为真且索引未对齐,所以对BitConverter的调用反转了字节的顺序,因此给出了一个不正确的值?
答案 0 :(得分:3)
该代码避免了不允许未对齐数据访问的处理器的硬件异常bus error。其中非常昂贵,它通常由内核代码解决,该代码分割总线访问并将字节粘合在一起。在编写代码的时候,这样的处理器仍然非常普遍,这是RISC设计如MIPS流行的尾声。较旧的ARM cores和Itanium是其他示例,已经为所有这些版本发布了.NET版本。
对于没有问题的处理器,如Intel / AMD内核,它几乎没什么区别。记忆很慢。
代码使用IsLittleEndian只是因为它索引了各个字节。这当然使字节顺序变得重要。
答案 1 :(得分:1)
在大多数架构上,在访问未在适当边界对齐的数据时会出现性能损失。在x86上,CPU将允许您从未对齐的地址读取,但会有性能损失。在某些架构上,您将遇到操作系统将陷阱的CPU故障。
我猜想让CPU修复未对齐数据的读取成本大于读取单个字节和进行移位/操作的成本。此外,代码现在可移植到未对齐读取将导致错误的平台。
答案 2 :(得分:1)
为什么无论机器的字节顺序如何都能正常工作?
该方法对byte
进行了重新解释,假设它们是在具有相同字节序的环境中生成的。换句话说,字节顺序既影响输入字节在数组中排列的顺序,又影响字节需要以相同方式排列在输出short
中的顺序。
为什么机器是Big Endian时它不会使用相同的机制?
这是一个很好的观察,并不是很明显为什么作者没有进行演员表演。我认为其背后的原因是,如果您将pbyte
的奇数值转换为short*
,则对short
的后续访问将是未对齐。这需要一个特殊的操作码来防止一些平台在未对齐访问时生成的硬异常。