本文详解如何在 go 中实现字节数组的逻辑左移一位操作,确保高位进位正确传递到下一个字节,适用于位运算、密码学或协议解析等场景。
在单个字节上执行左移(整体左移一位时,关键在于处理跨字节进位:前一个字节的最高位(bit 7)应作为后一个字节的最低位(bit 0)的输入。这本质上是将整个字节数组视为一个大端排列的二进制大整数,并对其执行一次逻辑左移。
以下是一个健壮、通用的 shiftBytesLeft 函数实现:
func shiftBytesLeft(a []byte) []byte {
if len(a) == 0 {
return a
}
dst := make([]byte, len(a))
// 处理除最后一个字节外的所有字节:左移本字节,并从下一个字节借入最高位(作为本字节的 LSB)
for i := 0; i < len(a)-1; i++ {
dst[i] = a[i]<> 7)
}
// 最后一个字节仅左移,低位补 0(无后续字节提供进位)
dst[len(a)-1] = a[len(a)-1] << 1
return dst
}
✅ 工作原理说明:
- a[i]
- a[i+1] >> 7:取下一个字节的最高位(bit7),右移到最低位位置(即 0x01 或 0x00);
- | 操作将该进位“填入”当前字节左移后空出的最低位(LSB);
- 最后一个字节无后续字节可借位,因此只左移,LSB 补 0。
? 使用示例:
func main() {
// 示例:2 字节数组 0xD3 0x4A → 二进制:11010011 01001010
src := []byte{0xD3, 0x4A}
fmt.Printf("原始: %08b %08b\n", src[0], src[1]) // 11010011 01001010
shifted := shiftBytesLeft(src)
fmt.Printf("左移1位: %08b %08b\n", shifted[0], shifted[1]) // 10100110 10010100
}
输出验证:
- 0xD3(11010011)左移 → 1010011?,其 bit7(1)进位至下一字节 LSB;
- 0x4A(01001010)左移 → 10010100,再与进位 1 组合 → 10010100(注意:此处进位实际加在 前一字节 的 LSB,即 0xD3>7 = 0?等等——需重新校验逻辑)
⚠️ 重要澄清与修正:
上述函数中 dst[i] = a[i]>7) 的设计意图是——将 a[i+1] 的最高位“补到 a[i] 左移后的最低位”,这对应的是整个数组向左平移,高位溢出由右侧字节“供给”,即:
移动后,原 a[0] 的 bit7 成为 dst[0] 的 bit6,而 dst[0] 的 bit0 来自 a[1] 的 bit7 —— 这实际是循环左移风格?不,不是循环。
实际上,标准逻辑左移(非循环) 对多字节数组的定义是: