我目前正在研究加密,我想出了反转字节半字节的方法(例如0xF5
=> 0x5F
)。我想出了这个解决方案:
byte >> 4 | (byte & 0x0F) << 4
我在网上找到的其他解决方案是类似的,但是通过屏蔽字节的左半字节添加了一个额外的操作数:
(byte & 0xF0) >> 4 | (byte & 0x0F) << 4
用二进制表示的话,第二个解决方案如下:
# Extract the right nibble of a byte and shift to the left
[0xF5] 1111 0101 # Original Value
[0x0F] 0000 1111 & # Mask Right Nibble
[0x05] 0000 0101 = # Extracted Right Nibble
[0x50] 1010 0000 << 4 # Shift Four Bits to the Left
# Extract the left nibble of a byte and shift to the right
[0xF5] 1111 0101 # Original Value
[0xF0] 1111 0000 & # Mask Left Nibble
[0xF0] 1111 0000 = # Extracted Left Nibble
[0x0F] 0000 1111 >> 4 # Shift Four Bits to the Right
# Combine the shifted nibbles together
[0x05] 0000 1111 # Left Nibble Shifted to the Right
[0xF0] 0101 0000 | # Right Nibble Shifted to the Left
[0xF5] 0101 1111 = # New Value
如果我错了,请纠正我,但如果您处理的数据类型大于一个字节并且仅关注最低有效字节,则第二种解决方案很有用。因此,如果您在不屏蔽的情况下进行移位,则高阶字节的位将传播到左半字节中。因此,在这种情况下,屏蔽左半字节是必要的。
# Bits shift into the least significant byte without masking
[0x0AF5] 0000 1010 1111 0101
[0x00AF] 0000 0000 1010 1111 >> 4
另一方面,如果您真正使用一个字节,那么屏蔽左半字节是否是多余的,因为在右移四位后左半字节位将被清零?
[0xF5] 1111 0101
[0x0F] 0000 1111 >> 4
也许还有其他我不知道的原因需要屏蔽左半字节。例如,其他系统在需要第二种解决方案时可能会有不同的表现。
我的想法是否正确,或者我是否应该考虑一些事情?
以下是进一步说明的示例代码:
typedef uint8_t byte;
static inline
byte swap_nibbles(byte bits) {
return bits >> 4 | (bits & 0x0F) << 4;
}
您没有显示 的定义
byte
。如果它有一个有符号的八位整数,则此代码:在许多 C 实现中打印:
尽管
byte
最初包含位 91 16,但在 中byte >> 4
,它被提升为int
。由于这些位代表值 −111,因此这将生成一个int
,其值为 ,其四字节为int
FFFFFF91 16。然后>> 4
生成 FFFFFF9 16 。(这是实现定义的。)将其与右侧的10 16|
进行或运算,生成 FFFFFF9 16,然后将其赋值给 ,将byte
其转换为signed char
。这是实现定义的,但最常见的结果是对 256 取模后包装,生成位 F9 16,表示值 −7。相反,使用
byte = (byte &0xF0) >> 4 | (byte & 0x0F) << 4;
印刷品:但是,如果
byte
具有无符号的八位整数类型,则提升为int
不是问题,因为int
有足够的空间来保存这些表达式中的值而不会遇到符号或溢出问题。现代编译器可能针对位操作进行了大量优化,因此这两种表达式很可能针对目标架构进行了很好的优化。您首先应该关注的是正确性。