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,常量除零则编译失败。
浮点运算¶
浮点遵循 IEEE 754 的有限精度语义。
不要用 == 判断一般计算结果是否“足够接近”,应根据业务尺度选择容差。
比较运算符¶
整数、浮点、字符串可按规则比较;布尔值只能比较相等或不等。
数组和字段均可比较的结构体可以使用 ==;切片、映射和函数不能互相比较,只能在允许时与 nil 比较。
字符串比较按字节字典序进行,不是自然语言排序:
面向用户的本地化排序需要专门的 Unicode/locale 处理方案。
逻辑运算符¶
&& 和 || 短路求值。
第二个操作数可能不执行,因此不要把重要副作用隐藏在复杂逻辑表达式中。
! 对布尔值取反,Go 不把整数、字符串或 nil 自动转换为真假值。
位运算符¶
位运算适合协议字段、权限位和底层数据处理,不应为了“优化”替代清晰的业务布尔字段。
赋值与复合赋值¶
Go 还支持 &=、|=、^=、&^=、<<=、>>=。
++、-- 只能作为语句,且只有后缀形式。
地址和解引用¶
& 在一元上下文取地址,在二元上下文执行按位与。
* 在一元上下文解引用,在二元上下文执行乘法;具体含义由语法位置决定。
差异示例一:整数除法与浮点除法¶
第一项结果为 2,第二项约为 2.3333。
仅转换最终结果 float64(7 / 3) 已经丢失小数,必须在除法前转换操作数。
差异示例二:逻辑与位运算¶
&& 只接受布尔值并短路;& 接受整数并总会求值两侧。
Go 不允许像 C 那样用整数 0/1 直接替代布尔条件。
差异示例三:^ 与 &^¶
a ^ b 将不同位设为 1;a &^ b 清除 a 中对应 b 为 1 的位。
权限撤销等场景中,位清除通常比异或更符合幂等语义。
运算符优先级¶
乘除和移位高于加减,加减高于比较,比较高于 &&,&& 高于 ||。
即使熟悉优先级,复杂混合表达式也应使用括号或中间变量表达意图。
溢出边界¶
运行时无符号整数按模运算回绕;有符号整数运算也可能产生按表示截断的结果。 常量溢出会在编译期被拒绝,但变量运算不会自动返回错误。 处理不可信长度、金额或容量时,运算前后都应验证范围。
常见错误¶
- 把整数除法结果转换为浮点,误以为能恢复小数。
- 用
==比较浮点计算结果或切片内容。 - 混淆
^、&^和逻辑非。 - 依赖字符串
<实现自然语言排序。 - 在长度相加或乘法后才检查上限,忽略运算已经溢出。
- 在短路表达式中隐藏必须发生的函数调用。
工程实践¶
- 单位和位宽明确,跨边界计算前检查输入范围。
- 内容比较使用
bytes.Equal、slices.Equal或领域比较函数。 - 浮点近似比较根据绝对误差和相对误差制定策略。
- 位掩码定义为有名称的类型和常量,不散布魔法数字。
- 对复杂条件使用命名布尔变量,提高日志和测试可读性。
- 安全敏感的容量计算使用能检测溢出的明确逻辑。
运算符本身简单,真正的风险通常来自类型、单位、溢出和外部数据边界。