跳转至

Go 语言运算符

Go 提供算术、比较、逻辑、位、赋值、地址和 channel 相关运算符。 本篇聚焦基础表达式的类型规则、求值边界和常见陷阱;channel 运算会在并发专题深入。

算术运算符

a, b := 7, 3
fmt.Println(a + b)
fmt.Println(a - b)
fmt.Println(a * b)
fmt.Println(a / b)
fmt.Println(a % b)

整数除法舍弃小数部分,结果仍是整数。 余数运算 % 只用于整数;除数为零会在运行时 panic,常量除零则编译失败。

浮点运算

x, y := 7.0, 3.0
fmt.Println(x / y)

浮点遵循 IEEE 754 的有限精度语义。 不要用 == 判断一般计算结果是否“足够接近”,应根据业务尺度选择容差。

比较运算符

==  !=  <  <=  >  >=

整数、浮点、字符串可按规则比较;布尔值只能比较相等或不等。 数组和字段均可比较的结构体可以使用 ==;切片、映射和函数不能互相比较,只能在允许时与 nil 比较。

字符串比较按字节字典序进行,不是自然语言排序:

fmt.Println("Z" < "a")
fmt.Println("10" < "2")

面向用户的本地化排序需要专门的 Unicode/locale 处理方案。

逻辑运算符

if ready && count > 0 {
    fmt.Println("run")
}

if cached || load() {
    fmt.Println("available")
}

&& 和 || 短路求值。 第二个操作数可能不执行,因此不要把重要副作用隐藏在复杂逻辑表达式中。 ! 对布尔值取反,Go 不把整数、字符串或 nil 自动转换为真假值。

位运算符

&   按位与
|   按位或
^   按位异或,作为一元运算时按位取反
&^  位清除
<<  左移
>>  右移
flags := uint8(0b1111)
mask := uint8(0b0101)
fmt.Printf("%04b\n", flags&^mask)

位运算适合协议字段、权限位和底层数据处理,不应为了“优化”替代清晰的业务布尔字段。

赋值与复合赋值

total := 10
total += 5
total *= 2

Go 还支持 &=、|=、^=、&^=、<<=、>>=。 ++、-- 只能作为语句,且只有后缀形式。

地址和解引用

value := 10
pointer := &value
fmt.Println(*pointer)

& 在一元上下文取地址,在二元上下文执行按位与。 * 在一元上下文解引用,在二元上下文执行乘法;具体含义由语法位置决定。

差异示例一:整数除法与浮点除法

fmt.Println(7 / 3)
fmt.Println(float64(7) / float64(3))

第一项结果为 2,第二项约为 2.3333。 仅转换最终结果 float64(7 / 3) 已经丢失小数,必须在除法前转换操作数。

差异示例二:逻辑与位运算

logical := true && false
bitwise := uint8(0b1100) & uint8(0b1010)
fmt.Println(logical, bitwise)

&& 只接受布尔值并短路;& 接受整数并总会求值两侧。 Go 不允许像 C 那样用整数 0/1 直接替代布尔条件。

差异示例三:^ 与 &^

a := uint8(0b1100)
b := uint8(0b1010)
fmt.Printf("%04b %04b\n", a^b, a&^b)

a ^ b 将不同位设为 1;a &^ b 清除 a 中对应 b 为 1 的位。 权限撤销等场景中,位清除通常比异或更符合幂等语义。

运算符优先级

乘除和移位高于加减,加减高于比较,比较高于 &&,&& 高于 ||。 即使熟悉优先级,复杂混合表达式也应使用括号或中间变量表达意图。

溢出边界

运行时无符号整数按模运算回绕;有符号整数运算也可能产生按表示截断的结果。 常量溢出会在编译期被拒绝,但变量运算不会自动返回错误。 处理不可信长度、金额或容量时,运算前后都应验证范围。

常见错误

  • 把整数除法结果转换为浮点,误以为能恢复小数。
  • 用 == 比较浮点计算结果或切片内容。
  • 混淆 ^、&^ 和逻辑非。
  • 依赖字符串 < 实现自然语言排序。
  • 在长度相加或乘法后才检查上限,忽略运算已经溢出。
  • 在短路表达式中隐藏必须发生的函数调用。

工程实践

  • 单位和位宽明确,跨边界计算前检查输入范围。
  • 内容比较使用 bytes.Equal、slices.Equal 或领域比较函数。
  • 浮点近似比较根据绝对误差和相对误差制定策略。
  • 位掩码定义为有名称的类型和常量,不散布魔法数字。
  • 对复杂条件使用命名布尔变量,提高日志和测试可读性。
  • 安全敏感的容量计算使用能检测溢出的明确逻辑。

运算符本身简单,真正的风险通常来自类型、单位、溢出和外部数据边界。