Appearance
位运算符
1. 概述
位运算符是 Go 语言中用于操作整数二进制位的运算符,它们直接对整数的二进制位进行操作,不进行任何类型转换。位运算符在底层编程、性能优化、位掩码等场景中非常常用,是理解计算机底层工作原理的重要工具。
本章节将详细介绍 Go 语言中位运算符的种类、使用方法、优先级以及相关的最佳实践,帮助学习者掌握位运算符的核心概念和使用技巧。
2. 学习建议
- 学习时间:建议分配 1-2 小时学习位运算符的基本概念和使用方法
- 学习方法:理论学习与实践相结合,每学习一个运算符后立即编写代码验证
- 学习重点:各种位运算符的使用方法、位操作的原理
- 学习难点:位掩码的使用、复杂位操作的理解
3. 前置知识要求
- 基础编程概念
- 计算机基础知识
- 了解二进制数的表示和运算
- 了解基本的数据类型
4. 学习目标
- 掌握 Go 语言中位运算符的种类和使用方法
- 理解位运算符的工作原理
- 能够正确使用位运算符进行位操作
- 了解位运算中的常见错误和避免方法
- 掌握位运算符的最佳实践
5. 基本概念
5.1 语法
5.1.1 基本位运算符
Go 语言中提供了以下基本位运算符:
| 运算符 | 描述 | 示例 |
|---|---|---|
| & | 按位与 | x & y |
| | | 按位或 | x | y |
| ^ | 按位异或 | x ^ y |
| << | 左移 | x << y |
| >> | 右移 | x >> y |
| ^ | 按位取反 | ^x |
示例代码:
go
func main() {
// 按位与
a := 1 & 2 // 二进制: 0001 & 0010 = 0000
fmt.Println("1 & 2 =", a) // 输出: 1 & 2 = 0
// 按位或
b := 1 | 2 // 二进制: 0001 | 0010 = 0011
fmt.Println("1 | 2 =", b) // 输出: 1 | 2 = 3
// 按位异或
c := 1 ^ 2 // 二进制: 0001 ^ 0010 = 0011
fmt.Println("1 ^ 2 =", c) // 输出: 1 ^ 2 = 3
// 左移
d := 1 << 2 // 二进制: 0001 << 2 = 0100
fmt.Println("1 << 2 =", d) // 输出: 1 << 2 = 4
// 右移
e := 4 >> 2 // 二进制: 0100 >> 2 = 0001
fmt.Println("4 >> 2 =", e) // 输出: 4 >> 2 = 1
// 按位取反
f := ^1 // 二进制: ^0001 = 1110 (补码表示)
fmt.Println("^1 =", f) // 输出: ^1 = -2
}5.2 语义
- 按位与运算符:对两个操作数的对应二进制位进行与操作,只有当两个对应位都为 1 时,结果位才为 1
- 按位或运算符:对两个操作数的对应二进制位进行或操作,只要有一个对应位为 1 时,结果位就为 1
- 按位异或运算符:对两个操作数的对应二进制位进行异或操作,当两个对应位不同时,结果位为 1
- 左移运算符:将左操作数的二进制位向左移动右操作数指定的位数,右侧用 0 填充
- 右移运算符:将左操作数的二进制位向右移动右操作数指定的位数,左侧用符号位填充(对于有符号数)或用 0 填充(对于无符号数)
- 按位取反运算符:对操作数的每个二进制位进行取反操作,0 变为 1,1 变为 0
5.3 规范
- 运算符优先级:按位取反运算符的优先级最高,其次是移位运算符,然后是按位与运算符,最后是按位或和按位异或运算符
- 结合性:位运算符的结合性是从左到右
- 操作对象:位运算符的操作对象必须是整数类型(int、uint、int8、uint8、int16、uint16、int32、uint32、int64、uint64)
- 符号位:对于有符号数,移位操作时符号位会被保留
6. 原理深度解析
6.1 位运算的实现原理
Go 语言中的位运算由编译器转换为相应的机器指令来实现。具体来说:
- 按位与运算:编译器会生成相应的位与指令
- 按位或运算:编译器会生成相应的位或指令
- 按位异或运算:编译器会生成相应的位异或指令
- 移位运算:编译器会生成相应的移位指令
- 按位取反运算:编译器会生成相应的位取反指令
示例代码:
go
func main() {
// 位运算的实现原理
x := 5 // 二进制: 0101
y := 3 // 二进制: 0011
fmt.Printf("x = %d (二进制: %04b)\n", x, x)
fmt.Printf("y = %d (二进制: %04b)\n", y, y)
fmt.Printf("x & y = %d (二进制: %04b)\n", x&y, x&y) // 按位与
fmt.Printf("x | y = %d (二进制: %04b)\n", x|y, x|y) // 按位或
fmt.Printf("x ^ y = %d (二进制: %04b)\n", x^y, x^y) // 按位异或
fmt.Printf("x << 1 = %d (二进制: %04b)\n", x<<1, x<<1) // 左移
fmt.Printf("x >> 1 = %d (二进制: %04b)\n", x>>1, x>>1) // 右移
fmt.Printf("^x = %d (二进制: %04b)\n", ^x, ^x) // 按位取反
}6.2 按位与运算的应用
按位与运算常用于以下场景:
- 位掩码:用于提取或清除特定的位
- 判断奇偶性:通过与 1 进行按位与运算,判断一个数的奇偶性
- 清除高位:通过与特定的掩码进行按位与运算,清除高位
示例代码:
go
func main() {
// 按位与运算的应用
// 1. 位掩码(提取特定位)
value := 0b10101010
mask := 0b00001111
result := value & mask
fmt.Printf("提取低 4 位: %08b & %08b = %08b\n", value, mask, result)
// 2. 判断奇偶性
num := 7
if num&1 == 1 {
fmt.Printf("%d 是奇数\n", num)
} else {
fmt.Printf("%d 是偶数\n", num)
}
// 3. 清除高位
value = 0b10101010
mask = 0b00001111
result = value & mask
fmt.Printf("清除高 4 位: %08b & %08b = %08b\n", value, mask, result)
}6.3 按位或运算的应用
按位或运算常用于以下场景:
- 设置特定位:用于将特定的位设置为 1
- 合并位:用于合并两个数的特定位
示例代码:
go
func main() {
// 按位或运算的应用
// 1. 设置特定位
value := 0b10100000
mask := 0b00001111
result := value | mask
fmt.Printf("设置低 4 位: %08b | %08b = %08b\n", value, mask, result)
// 2. 合并位
highBits := 0b10100000
lowBits := 0b00001010
result = highBits | lowBits
fmt.Printf("合并位: %08b | %08b = %08b\n", highBits, lowBits, result)
}6.4 按位异或运算的应用
按位异或运算常用于以下场景:
- 切换特定位:用于将特定的位切换(0 变 1,1 变 0)
- 交换变量:不使用临时变量交换两个变量的值
- 检测两个数是否同号:通过异或运算的符号位判断
示例代码:
go
func main() {
// 按位异或运算的应用
// 1. 切换特定位
value := 0b10101010
mask := 0b00001111
result := value ^ mask
fmt.Printf("切换低 4 位: %08b ^ %08b = %08b\n", value, mask, result)
// 2. 交换变量
a := 10
b := 20
fmt.Printf("交换前: a = %d, b = %d\n", a, b)
a ^= b
b ^= a
a ^= b
fmt.Printf("交换后: a = %d, b = %d\n", a, b)
// 3. 检测两个数是否同号
x := 10
y := -20
if (x ^ y) < 0 {
fmt.Printf("%d 和 %d 符号不同\n", x, y)
} else {
fmt.Printf("%d 和 %d 符号相同\n", x, y)
}
}6.5 移位运算的应用
移位运算常用于以下场景:
- 乘法和除法:左移相当于乘以 2 的幂,右移相当于除以 2 的幂
- 位操作:用于快速设置或清除特定位
- 数据压缩:用于压缩数据
示例代码:
go
func main() {
// 移位运算的应用
// 1. 乘法和除法
num := 10
fmt.Printf("%d << 1 = %d (相当于乘以 2)\n", num, num<<1)
fmt.Printf("%d >> 1 = %d (相当于除以 2)\n", num, num>>1)
// 2. 位操作
value := 1
fmt.Printf("设置第 3 位: %d << 2 = %d\n", value, value<<2)
// 3. 数据压缩(示例)
// 假设我们有 4 个 2 位的数,将它们压缩到一个字节中
a := 0b10 // 2
b := 0b01 // 1
c := 0b11 // 3
d := 0b00 // 0
compressed := (a << 6) | (b << 4) | (c << 2) | d
fmt.Printf("压缩后: %08b\n", compressed)
// 解压
a = (compressed >> 6) & 0b11
b = (compressed >> 4) & 0b11
c = (compressed >> 2) & 0b11
d = compressed & 0b11
fmt.Printf("解压后: a=%02b, b=%02b, c=%02b, d=%02b\n", a, b, c, d)
}6.6 按位取反运算的应用
按位取反运算常用于以下场景:
- 创建掩码:用于创建特定的掩码
- 位操作:用于反转特定位
示例代码:
go
func main() {
// 按位取反运算的应用
// 1. 创建掩码
// 创建一个低 4 位为 1 的掩码
mask := ^(^0 << 4)
fmt.Printf("低 4 位掩码: %08b\n", mask)
// 2. 位操作(反转特定位)
value := 0b10101010
result := value ^ mask
fmt.Printf("反转低 4 位: %08b ^ %08b = %08b\n", value, mask, result)
}7. 常见错误与踩坑点
7.1 符号位的处理
错误表现:按位取反或移位操作后,结果与预期不符
产生原因:不了解有符号数的符号位处理规则
解决方案:
- 对于需要无符号操作的场景,使用无符号整数类型
- 了解有符号数的补码表示
示例代码:
go
func main() {
// 符号位的处理
// 有符号数的按位取反
var x int8 = 1 // 二进制: 00000001
fmt.Printf("x = %d (二进制: %08b)\n", x, x)
fmt.Printf("^x = %d (二进制: %08b)\n", ^x, ^x) // 输出: -2 (二进制: 11111110)
// 无符号数的按位取反
var y uint8 = 1 // 二进制: 00000001
fmt.Printf("y = %d (二进制: %08b)\n", y, y)
fmt.Printf("^y = %d (二进制: %08b)\n", ^y, ^y) // 输出: 254 (二进制: 11111110)
// 有符号数的右移(符号位扩展)
var z int8 = -8 // 二进制: 11111000
fmt.Printf("z = %d (二进制: %08b)\n", z, z)
fmt.Printf("z >> 1 = %d (二进制: %08b)\n", z>>1, z>>1) // 输出: -4 (二进制: 11111100)
// 无符号数的右移(零扩展)
var w uint8 = 248 // 二进制: 11111000
fmt.Printf("w = %d (二进制: %08b)\n", w, w)
fmt.Printf("w >> 1 = %d (二进制: %08b)\n", w>>1, w>>1) // 输出: 124 (二进制: 01111100)
}7.2 位运算符与逻辑运算符的混淆
错误表现:代码逻辑错误,结果与预期不符
产生原因:混淆了位运算符和逻辑运算符的使用场景
解决方案:
- 位运算符用于整数的位操作,返回整数
- 逻辑运算符用于布尔值的组合,返回布尔值
示例代码:
go
func main() {
// 位运算符与逻辑运算符的混淆
// 错误:使用位运算符进行逻辑判断
a := true
b := false
// fmt.Println("a & b =", a & b) // 编译错误: invalid operation: a & b (mismatched types bool and bool)
// 正确:使用逻辑运算符进行逻辑判断
fmt.Println("a && b =", a && b) // 输出: false
// 错误:使用逻辑运算符进行位操作
x := 1
y := 2
// fmt.Println("x && y =", x && y) // 编译错误: invalid operation: x && y (mismatched types int and int)
// 正确:使用位运算符进行位操作
fmt.Println("x & y =", x & y) // 输出: 0
}7.3 位掩码的使用错误
错误表现:位掩码操作结果与预期不符
产生原因:不了解位掩码的使用方法
解决方案:
- 了解位掩码的原理和使用方法
- 为位掩码定义清晰的常量
- 使用注释说明位掩码的含义
示例代码:
go
func main() {
// 位掩码的使用
// 定义位掩码常量
const (
Read = 1 << iota // 0b0001
Write // 0b0010
Execute // 0b0100
Delete // 0b1000
)
// 组合权限
permissions := Read | Write
fmt.Printf("权限: %b\n", permissions) // 输出: 11
// 检查权限
if permissions&Read != 0 {
fmt.Println("有读取权限")
}
if permissions&Execute != 0 {
fmt.Println("有执行权限")
} else {
fmt.Println("无执行权限")
}
// 添加权限
permissions |= Execute
fmt.Printf("添加执行权限后: %b\n", permissions) // 输出: 111
// 移除权限
permissions &= ^Write
fmt.Printf("移除写入权限后: %b\n", permissions) // 输出: 101
}7.4 移位操作的越界
错误表现:移位操作结果与预期不符或导致运行时错误
产生原因:移位位数超过了类型的位数
解决方案:
- 确保移位位数不超过类型的位数
- 使用无符号整数类型进行移位操作
示例代码:
go
func main() {
// 移位操作的越界
// 安全的移位操作
var x uint8 = 1
fmt.Printf("x << 3 = %d\n", x<<3) // 输出: 8
// 可能导致问题的移位操作
// var y int8 = 1
// fmt.Printf("y << 7 = %d\n", y<<7) // 输出: -128
// fmt.Printf("y << 8 = %d\n", y<<8) // 移位位数超过类型位数,结果不确定
}8. 常见应用场景
8.1 位掩码
场景描述:使用位掩码表示权限、状态等标志位
使用方法:使用按位或运算符组合标志位,使用按位与运算符检查标志位
示例代码:
go
func main() {
// 位掩码的应用
// 定义权限位掩码
const (
Read = 1 << iota // 0b0001
Write // 0b0010
Execute // 0b0100
Delete // 0b1000
)
// 组合权限
userPermissions := Read | Write
adminPermissions := Read | Write | Execute | Delete
fmt.Printf("用户权限: %b\n", userPermissions)
fmt.Printf("管理员权限: %b\n", adminPermissions)
// 检查权限
if userPermissions&Read != 0 {
fmt.Println("用户有读取权限")
}
if userPermissions&Execute != 0 {
fmt.Println("用户有执行权限")
} else {
fmt.Println("用户无执行权限")
}
}8.2 位操作优化
场景描述:使用位操作优化性能,如乘法和除法
使用方法:使用左移和右移运算符进行快速的乘法和除法
示例代码:
go
func main() {
// 位操作优化
// 快速乘法(乘以 2 的幂)
num := 10
fmt.Printf("%d * 2 = %d\n", num, num<<1) // 输出: 20
fmt.Printf("%d * 4 = %d\n", num, num<<2) // 输出: 40
fmt.Printf("%d * 8 = %d\n", num, num<<3) // 输出: 80
// 快速除法(除以 2 的幂)
num = 80
fmt.Printf("%d / 2 = %d\n", num, num>>1) // 输出: 40
fmt.Printf("%d / 4 = %d\n", num, num>>2) // 输出: 20
fmt.Printf("%d / 8 = %d\n", num, num>>3) // 输出: 10
}8.3 位操作实现集合
场景描述:使用位操作实现小型集合,如整数集合
使用方法:使用位掩码表示集合,每个位表示一个元素是否存在
示例代码:
go
func main() {
// 位操作实现集合
// 定义集合(使用位掩码)
var set int = 0
// 添加元素(设置对应位)
add := func(element int) {
set |= 1 << element
}
// 删除元素(清除对应位)
remove := func(element int) {
set &= ^(1 << element)
}
// 检查元素是否存在(检查对应位)
contains := func(element int) bool {
return set&(1<<element) != 0
}
// 测试集合操作
add(0)
add(2)
add(4)
fmt.Printf("集合: %b\n", set) // 输出: 10101
fmt.Printf("包含 0: %t\n", contains(0)) // 输出: true
fmt.Printf("包含 1: %t\n", contains(1)) // 输出: false
fmt.Printf("包含 2: %t\n", contains(2)) // 输出: true
remove(2)
fmt.Printf("移除 2 后: %b\n", set) // 输出: 10001
fmt.Printf("包含 2: %t\n", contains(2)) // 输出: false
}8.4 位操作实现位字段
场景描述:使用位操作实现位字段,用于压缩数据
使用方法:使用移位和按位或运算符将多个小整数存储在一个大整数中
示例代码:
go
func main() {
// 位操作实现位字段
// 定义位字段结构
// 假设我们有以下字段:
// - 年龄(7 位,0-127)
// - 性别(1 位,0=男,1=女)
// - 权限(2 位,0=无,1=读,2=写,3=读写)
// 位字段偏移量
const (
ageOffset = 0
genderOffset = 7
permissionOffset = 8
)
// 位字段掩码
const (
ageMask = 0x7F // 7 位
genderMask = 0x1 // 1 位
permissionMask = 0x3 // 2 位
)
// 打包位字段
var data uint16 = 0
// 设置年龄
age := 25
data |= (age & ageMask) << ageOffset
// 设置性别(女)
gender := 1
data |= (gender & genderMask) << genderOffset
// 设置权限(读写)
permission := 3
data |= (permission & permissionMask) << permissionOffset
fmt.Printf("打包后: %016b\n", data)
// 解包位字段
unpackedAge := (data >> ageOffset) & ageMask
unpackedGender := (data >> genderOffset) & genderMask
unpackedPermission := (data >> permissionOffset) & permissionMask
fmt.Printf("解包后: 年龄=%d, 性别=%d, 权限=%d\n", unpackedAge, unpackedGender, unpackedPermission)
}8.5 位操作实现哈希函数
场景描述:使用位操作实现简单的哈希函数
使用方法:使用位操作(如异或、移位)处理输入数据,生成哈希值
示例代码:
go
func main() {
// 位操作实现哈希函数
// 简单的字符串哈希函数
hash := func(s string) uint32 {
var h uint32 = 0
for i := 0; i < len(s); i++ {
h = h<<5 - h + uint32(s[i])
}
return h
}
// 测试哈希函数
fmt.Printf("hash('hello') = %d\n", hash("hello"))
fmt.Printf("hash('world') = %d\n", hash("world"))
fmt.Printf("hash('hello world') = %d\n", hash("hello world"))
}9. 行业最佳实践
9.1 位掩码的定义
- 使用常量定义位掩码:为位掩码定义清晰的常量,提高代码可读性
- 使用 iota 生成位掩码:使用 iota 关键字生成位掩码,减少重复代码
- 添加注释:为位掩码添加注释,说明每个位的含义
示例代码:
go
func main() {
// 位掩码的定义
// 使用 iota 生成位掩码
const (
Read = 1 << iota // 0b0001
Write // 0b0010
Execute // 0b0100
Delete // 0b1000
)
fmt.Printf("Read: %b\n", Read)
fmt.Printf("Write: %b\n", Write)
fmt.Printf("Execute: %b\n", Execute)
fmt.Printf("Delete: %b\n", Delete)
}9.2 位操作的性能优化
- 使用位操作替代乘除:对于 2 的幂的乘除,使用移位操作替代
- 使用位操作替代取模:对于 2 的幂的取模,使用按位与操作替代
- 避免不必要的位操作:只在必要时使用位操作,避免过度优化
示例代码:
go
func main() {
// 位操作的性能优化
// 1. 使用移位操作替代乘除
num := 10
fmt.Printf("%d * 2 = %d\n", num, num<<1) // 比 num * 2 更快
fmt.Printf("%d / 2 = %d\n", num, num>>1) // 比 num / 2 更快
// 2. 使用按位与操作替代取模(仅适用于 2 的幂)
size := 8 // 2 的幂
index := 10
fmt.Printf("%d %% %d = %d\n", index, size, index&(size-1)) // 比 index % size 更快
}9.3 位操作的可读性
- 使用命名的常量:使用命名的常量替代魔法数字,提高代码可读性
- 添加注释:为复杂的位操作添加注释,说明操作的目的
- 封装位操作:将复杂的位操作封装为函数,提高代码可读性
示例代码:
go
func main() {
// 位操作的可读性
// 不好的做法:使用魔法数字
// value := 0b10101010
// result := value & 0b00001111
// 好的做法:使用命名的常量和注释
const (
Low4BitsMask = 0b00001111 // 低 4 位掩码
)
value := 0b10101010
result := value & Low4BitsMask // 提取低 4 位
fmt.Printf("提取低 4 位: %08b & %08b = %08b\n", value, Low4BitsMask, result)
}9.4 位操作的安全性
- 使用无符号整数:对于需要无符号操作的场景,使用无符号整数类型
- 边界检查:确保位操作不会导致溢出或未定义行为
- 测试:对复杂的位操作进行充分的测试
示例代码:
go
func main() {
// 位操作的安全性
// 使用无符号整数
var x uint8 = 1
fmt.Printf("x << 3 = %d\n", x<<3) // 安全的移位操作
// 边界检查
shift := 3
if shift < 0 || shift >= 8 {
fmt.Println("移位位数超出范围")
} else {
fmt.Printf("x << %d = %d\n", shift, x<<shift)
}
}10. 常见问题答疑(FAQ)
10.1 Q: Go 语言中位运算符的优先级是怎样的?
A: Go 语言中位运算符的优先级从高到低依次是:
- 按位取反运算符(^)
- 移位运算符(<<, >>)
- 按位与运算符(&)
- 按位异或运算符(^)
- 按位或运算符(|)
示例代码:
go
func main() {
// 位运算符的优先级
x := 1
y := 2
z := 3
// 优先级示例
fmt.Println("x | y & z =", x|y&z) // 等价于 x | (y & z),输出: 3
fmt.Println("x & y ^ z =", x&y^z) // 等价于 (x & y) ^ z,输出: 3
fmt.Println("x << y | z =", x<<y|z) // 等价于 (x << y) | z,输出: 7
}10.2 Q: Go 语言中如何使用位掩码?
A: 在 Go 语言中,可以使用以下步骤使用位掩码:
- 定义位掩码常量
- 使用按位或运算符(|)组合多个位掩码
- 使用按位与运算符(&)检查特定位是否设置
- 使用按位与运算符和按位取反运算符(&^)清除特定位
示例代码:
go
func main() {
// 使用位掩码
// 定义位掩码常量
const (
Read = 1 << iota // 0b0001
Write // 0b0010
Execute // 0b0100
Delete // 0b1000
)
// 组合位掩码
permissions := Read | Write
fmt.Printf("权限: %b\n", permissions) // 输出: 11
// 检查特定位
if permissions&Read != 0 {
fmt.Println("有读取权限")
}
// 清除特定位
permissions &^= Write
fmt.Printf("移除写入权限后: %b\n", permissions) // 输出: 1
}10.3 Q: Go 语言中如何判断一个数的奇偶性?
A: 在 Go 语言中,可以通过与 1 进行按位与运算来判断一个数的奇偶性。如果结果为 1,则为奇数;如果结果为 0,则为偶数。
示例代码:
go
func main() {
// 判断奇偶性
num := 7
if num&1 == 1 {
fmt.Printf("%d 是奇数\n", num)
} else {
fmt.Printf("%d 是偶数\n", num)
}
}10.4 Q: Go 语言中如何不使用临时变量交换两个数?
A: 在 Go 语言中,可以使用按位异或运算不使用临时变量交换两个数。
示例代码:
go
func main() {
// 不使用临时变量交换两个数
a := 10
b := 20
fmt.Printf("交换前: a = %d, b = %d\n", a, b)
a ^= b
b ^= a
a ^= b
fmt.Printf("交换后: a = %d, b = %d\n", a, b)
}10.5 Q: Go 语言中如何提取一个数的特定位?
A: 在 Go 语言中,可以使用按位与运算符和掩码来提取一个数的特定位。
示例代码:
go
func main() {
// 提取特定位
value := 0b10101010
mask := 0b00001111 // 提取低 4 位的掩码
result := value & mask
fmt.Printf("提取低 4 位: %08b & %08b = %08b\n", value, mask, result)
}10.6 Q: Go 语言中如何设置一个数的特定位?
A: 在 Go 语言中,可以使用按位或运算符和掩码来设置一个数的特定位。
示例代码:
go
func main() {
// 设置特定位
value := 0b10100000
mask := 0b00001111 // 设置低 4 位的掩码
result := value | mask
fmt.Printf("设置低 4 位: %08b | %08b = %08b\n", value, mask, result)
}11. 实战练习
11.1 基础练习
练习 1:基本位运算
题目:编写一个程序,使用位运算符进行基本的位操作。
解题思路:使用各种位运算符进行位操作,观察结果。
参考代码:
go
func main() {
// 基本位运算
x := 0b1010
y := 0b0110
fmt.Printf("x = %d (二进制: %04b)\n", x, x)
fmt.Printf("y = %d (二进制: %04b)\n", y, y)
fmt.Printf("x & y = %d (二进制: %04b)\n", x&y, x&y) // 按位与
fmt.Printf("x | y = %d (二进制: %04b)\n", x|y, x|y) // 按位或
fmt.Printf("x ^ y = %d (二进制: %04b)\n", x^y, x^y) // 按位异或
fmt.Printf("x << 1 = %d (二进制: %04b)\n", x<<1, x<<1) // 左移
fmt.Printf("x >> 1 = %d (二进制: %04b)\n", x>>1, x>>1) // 右移
fmt.Printf("^x = %d (二进制: %04b)\n", ^x, ^x) // 按位取反
}运行结果:
x = 10 (二进制: 1010)
y = 6 (二进制: 0110)
x & y = 2 (二进制: 0010)
x | y = 14 (二进制: 1110)
x ^ y = 12 (二进制: 1100)
x << 1 = 20 (二进制: 10100)
x >> 1 = 5 (二进制: 0101)
^x = -11 (二进制: ...11110101)11.2 进阶练习
练习 2:位掩码实现权限系统
题目:编写一个程序,使用位掩码实现一个简单的权限系统。
提示:
- 定义权限位掩码:Read、Write、Execute、Delete
- 实现添加权限、移除权限、检查权限的功能
参考代码:
go
func main() {
// 位掩码实现权限系统
// 定义权限位掩码
const (
Read = 1 << iota // 0b0001
Write // 0b0010
Execute // 0b0100
Delete // 0b1000
)
// 用户权限
userPermissions := Read | Write
// 打印权限
printPermissions := func(name string, permissions int) {
fmt.Printf("%s 权限: %b\n", name, permissions)
fmt.Printf(" - 读取: %t\n", permissions&Read != 0)
fmt.Printf(" - 写入: %t\n", permissions&Write != 0)
fmt.Printf(" - 执行: %t\n", permissions&Execute != 0)
fmt.Printf(" - 删除: %t\n", permissions&Delete != 0)
}
// 打印初始权限
printPermissions("用户", userPermissions)
// 添加执行权限
userPermissions |= Execute
fmt.Println("\n添加执行权限后:")
printPermissions("用户", userPermissions)
// 移除写入权限
userPermissions &^= Write
fmt.Println("\n移除写入权限后:")
printPermissions("用户", userPermissions)
}运行结果:
用户权限: 11
- 读取: true
- 写入: true
- 执行: false
- 删除: false
添加执行权限后:
用户权限: 111
- 读取: true
- 写入: true
- 执行: true
- 删除: false
移除写入权限后:
用户权限: 101
- 读取: true
- 写入: false
- 执行: true
- 删除: false11.3 挑战练习
练习 3:位操作实现位图
题目:编写一个程序,使用位操作实现一个简单的位图(Bitmap),用于存储整数集合。
提示:
- 实现添加元素、删除元素、检查元素是否存在的功能
- 实现集合的并集、交集、差集操作
参考代码:
go
func main() {
// 位操作实现位图
// 定义位图类型
type Bitmap uint32
// 添加元素
add := func(b *Bitmap, element int) {
*b |= 1 << element
}
// 删除元素
remove := func(b *Bitmap, element int) {
*b &^= 1 << element
}
// 检查元素是否存在
contains := func(b Bitmap, element int) bool {
return b&(1<<element) != 0
}
// 并集
union := func(a, b Bitmap) Bitmap {
return a | b
}
// 交集
intersection := func(a, b Bitmap) Bitmap {
return a & b
}
// 差集
difference := func(a, b Bitmap) Bitmap {
return a &^ b
}
// 测试位图操作
var set1 Bitmap
add(&set1, 0)
add(&set1, 2)
add(&set1, 4)
fmt.Printf("set1: %b\n", set1)
var set2 Bitmap
add(&set2, 2)
add(&set2, 3)
add(&set2, 5)
fmt.Printf("set2: %b\n", set2)
fmt.Printf("set1 包含 2: %t\n", contains(set1, 2))
fmt.Printf("set1 包含 3: %t\n", contains(set1, 3))
fmt.Printf("并集: %b\n", union(set1, set2))
fmt.Printf("交集: %b\n", intersection(set1, set2))
fmt.Printf("差集: %b\n", difference(set1, set2))
remove(&set1, 2)
fmt.Printf("set1 移除 2 后: %b\n", set1)
}运行结果:
set1: 10101
set2: 11010
set1 包含 2: true
set1 包含 3: false
并集: 11111
交集: 100
差集: 10001
set1 移除 2 后: 1000112. 知识点总结
12.1 核心要点
- 基本位运算符:按位与(&)、按位或(|)、按位异或(^)、左移(<<)、右移(>>)、按位取反(^)
- 位掩码:用于表示权限、状态等标志位
- 位操作优化:使用位操作替代乘除、取模等运算,提高性能
- 位操作应用:位掩码、位字段、集合实现、哈希函数等
- 符号位处理:了解有符号数的符号位处理规则
12.2 易错点回顾
- 符号位的处理:有符号数的按位取反和移位操作可能会产生意外结果
- 位运算符与逻辑运算符的混淆:位运算符用于整数的位操作,逻辑运算符用于布尔值的组合
- 位掩码的使用错误:不了解位掩码的使用方法可能会导致错误
- 移位操作的越界:移位位数超过类型位数可能会导致未定义行为
- 位操作的可读性:复杂的位操作可能会降低代码可读性
13. 拓展参考资料
13.1 官方文档链接
13.2 进阶学习路径建议
- 计算机基础:深入学习计算机底层原理
- 汇编语言:学习汇编语言,了解底层指令
- 性能优化:学习如何使用位操作进行性能优化
- 密码学:学习密码学中的位操作应用
13.3 相关资源
- Go by Example - Bitwise Operators
- Effective Go - Operators
- The Go Programming Language - 第 2 章:程序结构
- Hacker's Delight - 一本关于位操作的经典书籍
