














运算符是程序里最基础的计算工具。本篇会把 Go 的所有运算符过一遍,重点放在和其他语言不同的地方
最基本的数学运算:
| 运算符 | 含义 | 示例 |
|---|---|---|
+ | 加 | 1 + 2 = 3 |
- | 减 | 5 - 3 = 2 |
* | 乘 | 3 * 4 = 12 |
/ | 除 | 10 / 3 = 3 |
% | 取余 | 10 % 3 = 1 |
简单吧,但是除法要注意下
整数除法直接截断:
PRTCL // GO
fmt.Println(10 / 3) // 3,不是 3.333...
fmt.Println(10.0 / 3) // 3.3333333333333335
两个整数相除,结果还是整数,小数部分直接丢掉。要得到小数结果,必须有一方是浮点数:
PRTCL // GO
a := 10
b := 3
fmt.Println(float64(a) / float64(b)) // 3.3333333333333335
取余的结果符号和被除数一致:
PRTCL // GO
fmt.Println(10 % 3) // 1
fmt.Println(-10 % 3) // -1,结果是负数
fmt.Println(10 % -3) // 1,结果是正数
取余结果的正负,跟被除数(左边那个数)一致,和除数(右边)无关
+ 用于字符串拼接:
PRTCL // GO
a := "Hello"
b := "Go"
fmt.Println(a + ", " + b + "!") // Hello, Go!
Go 有 ++ 和 --,但和其他语言有个重要区别:
PRTCL // GO
a := 1
a++ // a = 2
a-- // a = 1
Go 的 ++ 和 -- 是语句,不是表达式,不能放在赋值右边:
PRTCL // GO
a := 1
b := a++ // ❌ 报错
也没有前置的 ++a:
只有 a++ 和 a--,而且只能单独成行。Go 的设计去掉了 i++ + ++i 这类让人困惑的写法
基本赋值:
PRTCL // GO
a := 10
a = 20 // 重新赋值
复合赋值,把运算和赋值合在一起:
PRTCL // GO
a := 10
a += 5 // a = a + 5 = 15
a -= 3 // a = a - 3 = 12
a *= 2 // a = a * 2 = 24
a /= 4 // a = a / 4 = 6
a %= 4 // a = a % 4 = 2
字符串也可以用 +=:
PRTCL // GO
s := "Hello"
s += ", Go!"
fmt.Println(s) // Hello, Go!
比较运算符的结果是布尔值 true 或 false:
| 运算符 | 含义 |
|---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
PRTCL // GO
fmt.Println(1 == 1) // true
fmt.Println(1 != 2) // true
fmt.Println(3 > 2) // true
fmt.Println(3 < 2) // false
只有相同类型才能比较:
PRTCL // GO
var a int = 1
var b int32 = 1
fmt.Println(a == b) // ❌ 报错,int 和 int32 不同类型
必须转换成同一类型再比较:
PRTCL // GO
fmt.Println(a == int(b)) // ✅ true
字符串比较按字典序:
PRTCL // GO
fmt.Println("abc" == "abc") // true
fmt.Println("abc" < "abd") // true,c < d
fmt.Println("abc" > "ab") // true,更长的字符串更大
浮点数比较要小心:
PRTCL // GO
a := 0.1 + 0.2
b := 0.3
fmt.Println(a == b) // false,精度问题
浮点数不能直接用 == 比较,应该判断差值是否足够小:
PRTCL // GO
import "math"
diff := math.Abs(a - b)
fmt.Println(diff < 1e-9) // true,认为相等
用于组合多个布尔条件:
| 运算符 | 含义 | 示例 |
|---|---|---|
&& | 与,两边都为 true 才是 true | true && false = false |
|| | 或,一边为 true 就是 true | true || false = true |
! | 非,取反 | !true = false |
PRTCL // GO
age := 20
hasID := true
// 成年且有证件
if age >= 18 && hasID {
fmt.Println("pass")
}
// 是管理员或者是超级用户
isAdmin := false
isSuperUser := true
if isAdmin || isSuperUser {
fmt.Println("pass")
}
// 不是访客
isGuest := false
if !isGuest {
fmt.Println("not guess")
}
短路求值
&& 和 || 都有短路特性:
PRTCL // GO
// && 左边是 false,右边不会执行
false && someFunc() // someFunc() 不会被调用
// || 左边是 true,右边不会执行
true || someFunc() // someFunc() 不会被调用
利用这个特性可以避免空指针等问题:
PRTCL // GO
// 先检查 user 不为空,再访问 user.Name
if user != nil && user.Name == "admin" {
fmt.Println("是管理员")
}
如果 user 是 nil,&& 短路,不会执行右边,就不会因为访问 nil 的字段而崩溃
位运算直接操作二进制位,在处理权限、标志位、底层协议时很常用:
| 运算符 | 含义 | 示例 |
|---|---|---|
& | 按位与 | 0101 & 0011 = 0001 |
| | 按位或 | 0101 | 0011 = 0111 |
^ | 按位异或 | 0101 ^ 0011 = 0110 |
<< | 左移 | 1 << 3 = 8 |
>> | 右移 | 8 >> 1 = 4 |
&^ | 按位清零(Go 特有) | 0101 &^ 0011 = 0100 |
左移和右移
左移一位相当于乘以 2,右移一位相当于除以 2:
PRTCL // GO
fmt.Println(1 << 3) // 8,1 × 2³
fmt.Println(8 >> 1) // 4,8 ÷ 2
上一篇 iota 里定义文件大小就是用的左移:
PRTCL // GO
const (
_ = iota
KB = 1 << (10 * iota) // 1024
MB // 1048576
GB // 1073741824
)
按位与用来检查标志位:
PRTCL // GO
const (
PermRead = 1 << iota // 001
PermWrite // 010
PermExecute // 100
)
perm := PermRead | PermWrite // 011,有读和写权限
// 检查是否有读权限
if perm & PermRead != 0 {
fmt.Println("有读权限")
}
// 检查是否有执行权限
if perm & PermExecute != 0 {
fmt.Println("有执行权限")
} else {
fmt.Println("没有执行权限")
}
&^ 按位清零,Go 特有
a &^ b 的意思是:把 b 中为 1 的位,在 a 中清零:
PRTCL // GO
perm := PermRead | PermWrite // 011
// 去掉写权限
perm = perm &^ PermWrite // 001
fmt.Println(perm & PermWrite != 0) // false,写权限没了
其他语言一般用 a & ~b 实现同样效果,Go 的这个操作单独成一个运算符
当一个表达式里有多个运算符时,优先级决定计算顺序。Go 的优先级从高到低:
| 优先级 | 运算符 |
|---|---|
| 5(最高) | * / % << >> & &^ |
| 4 | + - | ^ |
| 3 | == != < <= > >= |
| 2 | && |
| 1(最低) | || |
PRTCL // GO
fmt.Println(2 + 3*4) // 14,乘法优先
fmt.Println(2 > 1 && 3 > 2) // true,比较优先于 &&
不要依赖优先级,用括号写清楚:
PRTCL // GO
// 不清晰
result := a + b * c >> 2 & mask
// 清晰
result := ((a + (b * c)) >> 2) & mask
优先级规则不用背,遇到不确定的就加括号,清晰又不易错
^ 在 Go 里有两个用途
作为二元运算符是按位异或:
PRTCL // GO
fmt.Println(5 ^ 3) // 6,0101 ^ 0011 = 0110
作为一元运算符是按位取反(其他语言一般用 ~):
PRTCL // GO
var a uint8 = 5 // 00000101
fmt.Println(^a) // 250,11111010
整数除法和取余的关系
PRTCL // GO
a, b := 10, 3
q := a / b // 商 3
r := a % b // 余数 1
// 验证:q*b + r == a 永远成立
fmt.Println(q*b+r == a) // true
字符串不支持 -、*、/ 运算:
PRTCL // GO
"hello" - "world" // ❌ 报错
"hello" * 3 // ❌ 报错,Python 里可以但 Go 不行
字符串重复用 strings.Repeat():
PRTCL // GO
fmt.Println(strings.Repeat("go", 3)) // gogogo
下一篇讲 if 语句,条件判断的完整用法,以及 Go 里 if 特有的初始化语句写法
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。