Go 运算符详解

运算符就是程序里的加减乘除、比较、逻辑判断这些符号。Go 的运算符和 C/Java 非常像,熟悉的话几乎可以无缝上手,但 Go 也有几个自己的小特色,比如位清除 &^、以及自增自减只能当语句用。本篇按类别逐个讲清楚,并在末尾给出运算符优先级。

一、算术运算符

最基础的加减乘除,加上取余和自增自减。需要特别留意的是整数除法会截断小数——两个整数相除,结果还是整数,小数部分直接丢掉,不会四舍五入。想要小数,至少要有一个操作数是浮点数。

package main

import "fmt"

func main() {
    a, b := 17, 5
    fmt.Println(a+b, a-b, a*b) // 22 12 85
    fmt.Println(a / b)         // 3(整数除法,直接截断小数)
    fmt.Println(a % b)         // 2(取余)

    fmt.Println(7.0 / 2.0)     // 3.5(浮点数才保留小数)

    n := 1
    n++                        // 自增,等价于 n = n + 1
    n--                        // 自减
    fmt.Println(n)             // 1
}

还有一点和 C/Java 不同:Go 的 ++-- 只能作为语句,不能作为表达式。也就是说 b = a++ 这种写法在 Go 里是非法的,你只能单独写一行 a++。Go 也不区分前置和后置(++a 是错的),这避免了"自增写在前面还是后面"造成的混乱。

二、关系运算符

用来比较两个值,结果一定是布尔型 bool。一共六个:

package main

import "fmt"

func main() {
    a, b := 3, 5
    fmt.Println(a == b) // false
    fmt.Println(a != b) // true
    fmt.Println(a > b)  // false
    fmt.Println(a < b)  // true
    fmt.Println(a >= b) // false
    fmt.Println(a <= b) // true
}

注意:Go 强类型,不同类型的值不能直接比较(比如不能拿 intfloat64==),需要先显式转换。

三、逻辑运算符

用于组合多个布尔条件,结果还是布尔值:

&&|| 都有短路特性:左边能确定结果时就不再计算右边。比如 a != 0 && b/a > 1,当 a 为 0 时右边根本不会执行,从而避免了除以零的崩溃。利用短路可以写出既简洁又安全的判断。

package main

import "fmt"

func main() {
    age := 20
    hasID := true
    // && 两个都为真,结果才为真
    fmt.Println(age >= 18 && hasID) // true
    // || 只要有一个为真,结果就为真
    fmt.Println(age < 18 || !hasID) // false
}

四、位运算符

位运算直接操作二进制位,常用于底层、网络、加密、权限标志等场景。Go 支持的位运算符:

package main

import "fmt"

func main() {
    a, b := 0b1100, 0b1010 // 12 和 10(二进制字面量,Go 1.13+)
    fmt.Printf("a & b  = %b\n", a&b)  // 1000 按位与
    fmt.Printf("a | b  = %b\n", a|b)  // 1110 按位或
    fmt.Printf("a ^ b  = %b\n", a^b)  // 0110 按位异或
    fmt.Printf("a &^ b = %b\n", a&^b) // 0100 位清除(Go 特有)
    fmt.Printf("a << 2 = %b\n", a<<2) // 110000 左移 2 位
    fmt.Printf("a >> 1 = %b\n", a>>1) // 110 右移 1 位
}

五、赋值运算符

除了最基本的 =,Go 还提供了一堆"复合赋值"运算符,把运算和赋值合成一步,写起来更简洁:

package main

import "fmt"

func main() {
    var a int = 20
    a += 5  // a = a + 5
    a -= 3
    a *= 2
    a /= 4
    a %= 4
    a <<= 2
    a &= 6
    a ^= 2
    a |= 1
    fmt.Println(a)
}

a += 5 就是 a = a + 5 的简写,其余同理。复合赋值让代码更紧凑,也少写一次变量名。

六、其他运算符

还有几个常用但不属于上面分类的运算符,先认识一下:

package main

import "fmt"

func main() {
    x := 42
    p := &x          // & 取地址,p 是指向 x 的指针
    fmt.Println(*p)  // * 解引用,读取指针指向的值:42
    *p = 100         // 通过指针修改 x
    fmt.Println(x)   // 100

    // <- 是 channel 的收发运算符,留到并发篇详解
    // ch <- 1      // 向 channel 发送数据
    // v := <-ch    // 从 channel 接收数据
}

七、运算符优先级

从高到低排列(同一行内同级,按从左到右结合):

  1. * / % << >> & &^
  2. + - | ^
  3. == != < <= > >=
  4. &&
  5. ||

一元运算符(!、负号 -、取地址 &、解引用 *、接收 <-)优先级最高。优先级记不住完全没关系,合理地用括号让意图清晰,比死记优先级更可靠,代码也更易读。

小结

算术、关系、逻辑、位、赋值五大类是日常主力;整数除法截断小数、++ 只能当语句用是两个 Go 小规矩;位清除 &^ 是 Go 的小特色;指针和 channel 运算符先混个脸熟,后面会专门讲。下一篇进入条件语句

上一篇 Go 常量 | 下一篇 Go 条件语句

✈️💬