# Go 运算符

运算符是编程语言中用于执行操作的基本符号。Go 提供了丰富的运算符，包括算术、关系、逻辑、位运算和赋值运算符。本篇将逐一介绍各类运算符的用法，并配以代码示例。

## 算术运算符

Go 支持以下算术运算符：

| 运算符 | 说明 | 示例 |
|--------|------|------|
| `+` | 加法 | `a + b` |
| `-` | 减法 | `a - b` |
| `*` | 乘法 | `a * b` |
| `/` | 除法 | `a / b` |
| `%` | 取余 | `a % b` |
| `++` | 自增 | `a++` |
| `--` | 自减 | `a--` |

### 基本运算

```go
package main

import "fmt"

func main() {
    a, b := 17, 5

    fmt.Println("a + b =", a+b) // 22
    fmt.Println("a - b =", a-b) // 12
    fmt.Println("a * b =", a*b) // 85
    fmt.Println("a / b =", a/b) // 3（整数除法，截断小数部分）
    fmt.Println("a %% b =", a%b) // 2（取余）
}
```

### 整数除法与浮点除法

```go
package main

import "fmt"

func main() {
    // 整数除法会截断
    fmt.Println("10 / 3 =", 10/3)     // 3
    fmt.Println("10 %% 3 =", 10%3)     // 1

    // 浮点除法保留小数
    fmt.Println("10.0 / 3.0 =", 10.0/3.0) // 3.3333333333333335
}
```

### 自增与自减

Go 中的 `++` 和 `--` 是**语句**而非表达式，只能作为独立语句使用，不能放在表达式中：

```go
package main

import "fmt"

func main() {
    x := 10
    x++ // 等价于 x = x + 1
    fmt.Println("x++ 后:", x) // 11

    x-- // 等价于 x = x - 1
    fmt.Println("x-- 后:", x) // 10

    // 以下写法在 Go 中是非法的：
    // y := x++  // 编译错误
    // fmt.Println(x++)  // 编译错误
}
```

## 关系运算符

关系运算符用于比较两个值，结果为布尔类型（`true` 或 `false`）。

| 运算符 | 说明 | 示例 |
|--------|------|------|
| `==` | 等于 | `a == b` |
| `!=` | 不等于 | `a != b` |
| `<` | 小于 | `a < b` |
| `>` | 大于 | `a > b` |
| `<=` | 小于等于 | `a <= b` |
| `>=` | 大于等于 | `a >= b` |

```go
package main

import "fmt"

func main() {
    a, b := 10, 20

    fmt.Println("a == b:", a == b) // false
    fmt.Println("a != b:", a != b) // true
    fmt.Println("a < b:", a < b)   // true
    fmt.Println("a > b:", a > b)   // false
    fmt.Println("a <= b:", a <= b) // true
    fmt.Println("a >= b:", a >= b) // false

    // 字符串也可以比较（按字典序）
    s1, s2 := "apple", "banana"
    fmt.Println("s1 < s2:", s1 < s2) // true
    fmt.Println("s1 == s2:", s1 == s2) // false
}
```

## 逻辑运算符

逻辑运算符用于组合布尔表达式：

| 运算符 | 说明 | 示例 |
|--------|------|------|
| `&&` | 逻辑与（短路求值） | `a && b` |
| `\|\|` | 逻辑或（短路求值） | `a \|\| b` |
| `!` | 逻辑非 | `!a` |

### 短路求值

`&&` 和 `||` 具有短路特性：如果第一个操作数已经能确定结果，就不会计算第二个操作数。

```go
package main

import "fmt"

func main() {
    a, b := true, false

    fmt.Println("a && b:", a && b) // false
    fmt.Println("a || b:", a || b) // true
    fmt.Println("!a:", !a)         // false

    // 短路求值的实际应用
    age := 20
    hasID := true

    // 如果 hasID 为 false，不会检查 age
    if hasID && age >= 18 {
        fmt.Println("允许进入")
    }

    // 空指针保护
    var name *string
    if name != nil && *name != "" {
        fmt.Println("名字:", *name)
    } else {
        fmt.Println("名字为空")
    }
}
```

## 位运算符

位运算符直接操作整数的二进制位：

| 运算符 | 说明 | 示例 |
|--------|------|------|
| `&` | 按位与 | `a & b` |
| `\|` | 按位或 | `a \| b` |
| `^` | 按位异或 | `a ^ b` |
| `&^` | 按位清除（AND NOT） | `a &^ b` |
| `<<` | 左移 | `a << n` |
| `>>` | 右移 | `a >> n` |

### 基本位运算

```go
package main

import "fmt"

func main() {
    a := 12 // 二进制: 1100
    b := 10 // 二进制: 1010

    fmt.Printf("a & b  = %d (二进制: %04b)\n", a&b, a&b)     // 8, 1000
    fmt.Printf("a | b  = %d (二进制: %04b)\n", a|b, a|b)     // 14, 1110
    fmt.Printf("a ^ b  = %d (二进制: %04b)\n", a^b, a^b)     // 6, 0110
    fmt.Printf("a &^ b = %d (二进制: %04b)\n", a&^b, a&^b)   // 4, 0100
}
```

### 位移运算

```go
package main

import "fmt"

func main() {
    x := 8 // 二进制: 1000

    fmt.Printf("x << 1 = %d (二进制: %04b)\n", x<<1, x<<1) // 16, 10000
    fmt.Printf("x << 2 = %d (二进制: %04b)\n", x<<2, x<<2) // 32, 100000
    fmt.Printf("x >> 1 = %d (二进制: %04b)\n", x>>1, x>>1) // 4, 0100
    fmt.Printf("x >> 2 = %d (二进制: %04b)\n", x>>2, x>>2) // 2, 0010
}
```

### 位运算的实际应用

```go
package main

import "fmt"

func main() {
    // 判断奇偶
    n := 7
    if n&1 == 1 {
        fmt.Println(n, "是奇数")
    }

    // 用位运算实现权限标志
    const (
        Read    = 1 << iota // 1 (001)
        Write               // 2 (010)
        Execute             // 4 (100)
    )

    // 设置权限
    perm := Read | Write // 011
    fmt.Printf("当前权限: %03b\n", perm)

    // 检查是否有某个权限
    if perm&Read != 0 {
        fmt.Println("有读权限")
    }
    if perm&Execute == 0 {
        fmt.Println("没有执行权限")
    }

    // 添加权限
    perm |= Execute
    fmt.Printf("添加执行权限后: %03b\n", perm)

    // 移除权限
    perm &^= Write
    fmt.Printf("移除写权限后: %03b\n", perm)
}
```

## 赋值运算符

| 运算符 | 说明 | 等价写法 |
|--------|------|----------|
| `=` | 赋值 | `a = b` |
| `+=` | 加后赋值 | `a = a + b` |
| `-=` | 减后赋值 | `a = a - b` |
| `*=` | 乘后赋值 | `a = a * b` |
| `/=` | 除后赋值 | `a = a / b` |
| `%=` | 取余后赋值 | `a = a % b` |
| `&=` | 按位与后赋值 | `a = a & b` |
| `\|=` | 按位或后赋值 | `a = a \| b` |
| `^=` | 按位异或后赋值 | `a = a ^ b` |
| `<<=` | 左移后赋值 | `a = a << b` |
| `>>=` | 右移后赋值 | `a = a >> b` |

```go
package main

import "fmt"

func main() {
    x := 100

    x += 10 // x = x + 10
    fmt.Println("+= 10:", x) // 110

    x -= 30 // x = x - 30
    fmt.Println("-= 30:", x) // 80

    x *= 2 // x = x * 2
    fmt.Println("*= 2:", x) // 160

    x /= 4 // x = x / 4
    fmt.Println("/= 4:", x) // 40

    x %= 7 // x = x % 7
    fmt.Println("%%= 7:", x) // 5
}
```

## 运算符优先级

从高到低排列：

| 优先级 | 运算符 |
|--------|--------|
| 5（最高） | `*`, `/`, `%`, `<<`, `>>`, `&`, `&^` |
| 4 | `+`, `-`, `\|`, `^` |
| 3 | `==`, `!=`, `<`, `<=`, `>`, `>=` |
| 2 | `&&` |
| 1（最低） | `\|\|` |

### 优先级示例

```go
package main

import "fmt"

func main() {
    // 乘除优先于加减
    a := 2 + 3*4       // 2 + 12 = 14
    fmt.Println("2+3*4 =", a)

    // 关系运算符优先于逻辑运算符
    b := 10 > 5 && 3 < 7  // (10>5) && (3<7) → true && true → true
    fmt.Println("10>5 && 3<7 =", b)

    // 使用括号提高可读性
    c := (2 + 3) * 4     // 5 * 4 = 20
    fmt.Println("(2+3)*4 =", c)
}
```

> **建议**：当表达式涉及多个运算符时，尽量使用括号明确优先级，提高代码可读性。

## 总结

本篇介绍了 Go 语言中的各类运算符。算术运算符用于数学计算，关系运算符用于比较，逻辑运算符用于组合布尔条件（支持短路求值），位运算符直接操作二进制位（常用于权限控制和底层优化），赋值运算符提供了简洁的复合赋值方式。理解运算符的优先级有助于避免逻辑错误，但更推荐的做法是使用括号让表达式的意图更加清晰。

