我正在编写一个允许我在字节和各种整数数据类型之间进行转换的类。我没有反转数组然后转换数据,而是选择确定系统的字节顺序是否与数据相同。如果是,我只是将数据映射到整数,就像64位整数一样:
result = (long)(
(buffer[index] << 56) |
(buffer[index + 1] << 48) |
(buffer[index + 2] << 40) |
(buffer[index + 3] << 32) |
(buffer[index + 4] << 24) |
(buffer[index + 5] << 16) |
(buffer[index + 6] << 8) |
(buffer[index + 7]));
如果系统和数据的字节顺序不同,那么就会反过来:
result = (long)(
(buffer[index]) |
(buffer[index + 1] << 8) |
(buffer[index + 2] << 16) |
(buffer[index + 3] << 24) |
(buffer[index + 4] << 32) |
(buffer[index + 5] << 40) |
(buffer[index + 6] << 48) |
(buffer[index + 7] << 56));
result
是一个64位有符号整数
buffer
是一个字节数组
index
是一个32位有符号整数,表示缓冲区中开始读取的位置
我的问题是......我这样做是错误的还是这只是一种非常简单的转换方式而无需将数组转换到位或制作副本?
这似乎应该适用于系统和数据字节序的所有组合,并在两者之间正确转换。
是否有一种不同的方式可能更容易阅读或更简单?
答案 0 :(得分:1)
不是反转数组,而是可以反转long
(使用ulong
稍微容易一点):
ulong raw = BitConverter.ToUInt64(array, pos);
if (wrong_endian)
{
// swap groups of 4
raw = (raw >> 32) | (raw << 32);
// swap groups of 2
raw = ((raw >> 16) & 0x0000FFFF0000FFFF) | ((raw << 16) &0xFFFF0000FFFF0000);
// swap groups of 1
raw = ((raw >> 8) & 0x00FF00FF00FF00FF) | ((raw << 8) & 0xFF00FF00FF00FF00);
}
代码未经过测试,但您明白了。反转整数而不是数组。
答案 1 :(得分:0)
您可以轻松使用BitConverter课程。
这是一个直接来自here的例子。
byte[] bytes = { 0, 0, 0, 25 };
// If the system architecture is little-endian (that is, little end first),
// reverse the byte array.
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
int i = BitConverter.ToInt32(bytes, 0);
Console.WriteLine("int: {0}", i);
// Output: int: 25
答案 2 :(得分:0)
是的,你正确地做到了。 (评论中提到的bug除外)
代码很简单,但可能不简短。如果你想要更少的行,这将是:
result = 0;
for(var i = 0; i < 8; i++)
result |= (long)buffer[index + i] << (8*i);
希望编译器能够循环展开。与其他代码类似:
result = 0;
for(var i = 0; i < 8; i++)
result |= (long)buffer[index + i] << (56 - 8*i);
答案 3 :(得分:0)
在整数及其字节表示之间进行转换时有两种主要方案:
在与本机代码进行互操作时通常会出现这种情况。使用自然使用本机字节序的代码,例如Buffer.BlockCopy
,BitConverter.ToBytes
/ ToInt64
和不安全的代码。在某些情况下,p / invoke marshaller可以为你完成大部分工作。
解析文件或网络协议时通常就是这种情况。在这种情况下,您的代码片段(减去投射错误)是处理它的理想方式。给他们一个提到字节序的名称,例如ToInt64BitEndian
。
它们易于理解,易于测试(不依赖于系统字节序)并且速度相当快。
有时它可以提高性能,使用Buffer.BlockCopy
或不安全的重新解释转换,但我只会在分析后使用那些表明此代码中的瓶颈。在我的程序中,这从来就不是瓶颈,所以我使用的代码非常类似于你的例子。
我不喜欢基于逆向的代码,因为big-endian系统的代码路径不会在典型的little-endian系统上运行。
ErrataRob的code review of silent circle提出了类似的观点,详细阐述了一点:
协议解析与CPU无关。根据CPU的不同,没有理由做不同的事情。
转换和字节交换
上面执行
#if
条件的错误来自于尝试修复char*
和int*
之间的基本错误。这是“UNIX网络编程”课程中教授的常用技术。这也是错的。解析数据包时不应该这样做。有两个原因可以避免这种情况。第一个是(如上所述)某些CPU,例如SPARC和某些版本的ARM在引用未对齐的整数时崩溃。这使得网络代码在RISC系统上不稳定,因为大多数整数通常都是对齐的,这意味着许多对齐问题无法检测到运输代码。制作稳定代码的唯一方法是停止在网络(或文件)解析器中转换整数。
第二个问题是它会导致与字节顺序/字节顺序混淆,如果你只是不转换整数就不会发生这种情况。考虑IP地址“10.1.2.3”。此数字只有两种形式,可以是值为
0x0a010203
的整数,也可以是值为0a 01 02 03的字节数组。问题是小端机器很奇怪。整数0x0a010203
在x86处理器上内部表示为03 02 01 0a
,字节顺序为“交换”。但这只是一个你不必担心的内部细节。只要你从不跨越流并从
char*
转换为int*
(或反向),那么字节顺序/字节顺序就不重要了。