马克的答案似乎已经解决了OP最初的问题,因为它已被接受。但是,从问题文本中并不清楚为什么会这样。这仍然需要交换字节,而不是像马克的答案一样交换字节对。我不知道有任何合理常见的情况下按16位一次地交换比特是有意义或有用的。
根据所述要求,我认为写成以下形式更有意义:
int target = 250; // 0x00FA
// swap the bytes of target
target = ((target << 8) | (target >> 8)) & 0xFFFF;
// target now is 0xFA00
string hexString = target.ToString("X4");
请注意,上述假设我们实际处理的是存储在32位int变量中的16位值。它将处理16位范围内的任何输入(请注意需要屏蔽掉上16位,因为它们会被<<运算符设置为非零值)。
如果要交换32位值,则需要使用以下代码:
int target = 250;
target = (int)((int)((target << 24) & 0xff000000) |
((target << 8) & 0xff0000) |
((target >> 8) & 0xff00) |
((target >> 24) & 0xff));
string hexString = target.ToString("X8");
需要掩码才能隔离我们要移动到特定位置的位。在与其他三个字节或之前,将<< 24
的结果转换回int
是必需的,因为0xff000000
是一个uint
(UInt32
) 字面量,并导致 &
表达式扩展为long
(Int64
)。否则,每个|
运算符都会产生编译器警告。
无论如何,由于这在大多数网络场景中经常出现,值得注意的是,.NET提供了可以帮助完成此操作的辅助方法:HostToNetworkOrder()
和NetworkToHostOrder()
。在这个上下文中,“网络顺序”始终是大端字节序,“主机顺序”是用于托管当前进程的计算机上使用的任何字节顺序。
如果您知道您正在接收的数据是大端字节序,并且想要能够在您的进程中正确解释这些数据,那么您可以调用NetworkToHostOrder()
。同样地,如果您需要在期望大端字节序的上下文中发送数据,则可以调用HostToNetworkOrder()
。
这些方法仅适用于三种基本整数类型:Int16
、Int32
和Int64
(在C# 中分别为short
、int
和long
)。它们还返回传递给它们的相同类型,因此必须小心符号扩展。原始问题中的示例可以这样解决:
int target = 250; // 0x00FA
// swap the bytes of target
target = IPAddress.HostToNetworkOrder((short)target) & 0xFFFF;
// target now is 0xFA00
string hexString = target.ToString("X4");
再次强调,必须进行掩码处理,否则该方法返回的
short
值将被符号扩展为32位。如果结果中设置了第15位(即
0x8000
),那么最终的
int
值也将有其最高的16位设置。可以通过使用更合适的数据类型(例如当数据预期为带符号16位值时使用
short
)来解决此问题,而无需进行掩码处理。
最后,我要指出的是,由于
HostToNetworkOrder()
和
NetworkToHostOrder()
方法只交换字节,因此它们彼此等效。当机器架构为小端
†时,它们都交换字节。实际上,.NET实现仅仅是让
NetworkToHostOrder()
调用
HostToNetworkOrder()
。存在这两个方法主要是为了使.NET API与原始的BSD sockets API相匹配,该API包括诸如
htons()
和
ntohs()
之类的函数,并且该API又包括了用于转换方向的两个函数,主要是为了在代码中清楚地表明是否从网络接收数据或向网络发送数据。
† 当机器架构为大端时,它们不起作用...它们不是通用的字节交换函数。相反,期望网络协议始终为大端,并且使用这些函数来确保数据字节与机器架构匹配。
int value = 250;int otherEndian = (value << 16) | ((int)(((uint)value) >> 16));value = otherEndian;otherEndian = (value << 16) | ((int)(((uint)value) >> 16));
第二和第三行是我测试重复运行相同步骤是否会给我返回250。 - Brian