0

我正在编写一个类,它可以让我在字节和各种整数数据类型之间进行转换。我没有选择反转数组然后转换数据,而是选择确定系统的字节顺序是否与数据相同。如果是,我只需将数据映射到整数,例如 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 位有符号整数,指示缓冲区中开始读取的位置

我的问题是......我做错了还是这只是一种非常简单的转换方法,而无需反转数组或制作副本?

这似乎应该适用于系统和数据字节序的所有组合,并在两者之间正确转换。

是否有可能更容易阅读或更简单的不同方式?

4

4 回答 4

1

您可以反转数组,而不是反转数组long(使用 a 会稍微容易一些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);
}

代码没有经过测试,但你明白了。反转整数而不是数组。

于 2013-04-13T09:15:43.420 回答
0

您可以轻松使用BitConverter类。

这是一个直接来自这里的示例。

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
于 2013-04-13T08:59:43.500 回答
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);
于 2013-04-13T09:11:52.737 回答
0

在整数及其字节表示之间进行转换时,主要有两种情况:

原生字节序

这通常是与本机代码互操作时的情况。使用自然使用原生字节序的代码,例如Buffer.BlockCopy, BitConverter.ToBytes/ToInt64和不安全的代码。在某些情况下,p/invoke marshaller 可以为您完成大部分工作。

固定字节序

这通常是解析文件或网络协议时的情况。在这种情况下,您的代码片段(减去转换错误)是处理它的理想方式。给他们一个提及字节序的名称,例如ToInt64BitEndian.

它们易于理解、易于测试(不依赖于系统字节序)并且速度相当快。

有时它可以提高使用Buffer.BlockCopy或不安全的重新解释强制转换的性能,但我只会在分析后使用那些表明此代码存在瓶颈的。在我的程序中,这从来都不是瓶颈,所以我使用的代码与您的示例非常相似。

我不喜欢为此使用基于反转的代码,因为大端系统的代码路径不会在典型的小端系统上运行。


ErrataRob对沉默圈的代码审查提出了类似的观点,详细说明了一点:

协议解析与 CPU 无关。没有理由根据 CPU 做不同的事情。

强制转换和字节交换

执行上述#if条件的错误来自于试图修复在char*and之间转换的潜在错误int*。这是“UNIX 网络编程”课程中教授的一种常用技术。这也是错误的。解析数据包时永远不要这样做。

避免这种情况有两个原因。首先是(如上所述)某些 CPU,例如 SPARC 和某些版本的 ARM 在引用未对齐的整数时会崩溃。这使得 RISC 系统上的网络代码不稳定,因为大多数整数通常无论如何都是对齐的,这意味着许多对齐问题会在未检测到的情况下逃逸到运输代码中。制作稳定代码的唯一方法是停止在网络(或文件)解析器中转换整数。

第二个问题是它会导致与字节顺序/字节序混淆,如果您不强制转换整数,则不会发生这种情况。考虑 IP 地址“10.1.2.3”。这个数字只有两种形式,要么是值为 的整数,要么是0x0a010203值为 0a 01 02 03 的字节数组。问题是小端机器很奇怪。整数0x0a010203在内部表示为03 02 01 0ax86 处理器,字节顺序“交换”。

但这只是您永远不需要担心的内部细节。只要您从不跨越流并从 achar*转换为 a int*(或相反),那么字节顺序/字节序就无关紧要。

于 2013-04-13T09:53:07.513 回答