# Go 控制流

控制流决定了代码的执行顺序。Go 提供了 `if`、`for`、`switch` 等控制流语句，语法简洁而强大。值得一提的是，Go 只有 `for` 循环，没有 `while` 和 `do-while`。本篇将详细讲解 Go 中各种控制流语句的用法。

![控制流程](https://img.zhaojq.top/20260729161337213.png "控制流程")

## if 语句

### if-else 与多分支

```go
package main

import "fmt"

func main() {
    age := 16
    if age >= 18 {
        fmt.Println("成年人")
    } else {
        fmt.Println("未成年人")
    }

    score := 85
    if score >= 90 {
        fmt.Println("优秀")
    } else if score >= 80 {
        fmt.Println("良好")
    } else if score >= 60 {
        fmt.Println("及格")
    } else {
        fmt.Println("不及格")
    }
}
```

### if 带初始化语句

Go 的 `if` 支持在条件前加一个初始化语句，变量的作用域限定在 `if` 代码块内：

```go
package main

import (
    "fmt"
    "math/rand"
)

func main() {
    // 初始化语句中声明的变量仅在 if 块内可见
    if n := rand.Intn(100); n%2 == 0 {
        fmt.Printf("%d 是偶数\n", n)
    } else {
        fmt.Printf("%d 是奇数\n", n)
    }

    // fmt.Println(n) // 编译错误：n 未定义
}
```

## for 语句

Go 只有一种循环结构：`for`。但它可以实现其他语言中 `while`、`for`、无限循环的功能。

### 经典 for 循环（三要素）

```go
package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Printf("第 %d 次循环\n", i+1)
    }
}
```

### 条件 for（相当于 while）

省略初始化和后置语句，只保留条件：

```go
package main

import "fmt"

func main() {
    n := 1

    for n < 100 { // 相当于 while n < 100
        n *= 2
    }

    fmt.Println("结果:", n) // 128
}
```

### 无限 for

省略所有三个部分，形成无限循环：

```go
package main

import "fmt"

func main() {
    count := 0

    for {
        count++
        if count >= 5 {
            break
        }
        fmt.Println("循环中...", count)
    }

    fmt.Println("循环结束")
}
```

## range 关键字

`range` 是 Go 中用于遍历数据结构的关键字，可以用于数组、切片、字符串、map 和 channel。

### 遍历数组/切片

```go
package main

import "fmt"

func main() {
    fruits := []string{"苹果", "香蕉", "橙子", "葡萄"}

    // 获取索引和值
    for i, fruit := range fruits {
        fmt.Printf("[%d] %s\n", i, fruit)
    }

    // 只获取值（忽略索引）
    for _, fruit := range fruits {
        fmt.Println(fruit)
    }

    // 只获取索引
    for i := range fruits {
        fmt.Println("索引:", i)
    }
}
```

## switch 语句

Go 的 `switch` 比 C 语言更灵活，不需要写 `break`（自动终止），且条件不限于整数。

### 基本 switch

```go
package main

import "fmt"

func main() {
    day := "Monday"

    switch day {
    case "Monday":
        fmt.Println("星期一")
    case "Tuesday":
        fmt.Println("星期二")
    case "Wednesday":
        fmt.Println("星期三")
    case "Thursday":
        fmt.Println("星期四")
    case "Friday":
        fmt.Println("星期五")
    default:
        fmt.Println("周末")
    }
}
```

### 一个 case 匹配多个值

```go
package main

import "fmt"

func main() {
    month := 3

    switch month {
    case 3, 4, 5:
        fmt.Println("春天")
    case 6, 7, 8:
        fmt.Println("夏天")
    case 9, 10, 11:
        fmt.Println("秋天")
    case 12, 1, 2:
        fmt.Println("冬天")
    default:
        fmt.Println("无效月份")
    }
}
```

### 无条件 switch（相当于多个 if-else）

```go
package main

import "fmt"

func main() {
    score := 85

    switch {
    case score >= 90:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 60:
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }
}
```

### fallthrough

Go 的 `switch` 默认不穿透（自动 break），如果需要穿透到下一个 case，必须显式使用 `fallthrough`：

```go
package main

import "fmt"

func main() {
    n := 2

    switch n {
    case 1:
        fmt.Println("是一")
    case 2:
        fmt.Println("是二")
        fallthrough // 继续执行下一个 case
    case 3:
        fmt.Println("也会执行这里（fallthrough）")
    case 4:
        fmt.Println("不会执行这里")
    }
}
```

> **注意**：`fallthrough` 只能放在 case 的最后一行，且不会判断下一个 case 的条件。

## break 和 continue

### break

`break` 用于跳出最近的 `for`、`switch` 或 `select` 语句：

```go
package main

import "fmt"

func main() {
    // 找到第一个能被 7 整除的数就停止
    for i := 1; i <= 100; i++ {
        if i%7 == 0 {
            fmt.Println("找到:", i)
            break
        }
    }
}
```

### continue

`continue` 跳过当前循环的剩余部分，进入下一次迭代：

```go
package main

import "fmt"

func main() {
    // 只打印奇数
    for i := 1; i <= 10; i++ {
        if i%2 == 0 {
            continue // 跳过偶数
        }
        fmt.Print(i, " ")
    }
    fmt.Println()
    // 输出: 1 3 5 7 9
}
```

### 标签（Label）

Go 支持使用标签来配合 `break` 和 `continue` 跳出多层嵌套循环：

```go
package main

import "fmt"

func main() {
    // 使用标签跳出嵌套循环
outer:
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            if i*j > 6 {
                fmt.Printf("找到 i=%d, j=%d, 乘积=%d\n", i, j, i*j)
                break outer // 跳出外层循环
            }
        }
    }
}
```

## goto 语句

Go 保留了 `goto` 语句，但一般不推荐使用。它可以在同一函数内跳转到指定标签：

```go
package main

import "fmt"

func main() {
    // 简单演示 goto（实际开发中不推荐）
    count := 0

loop:
    count++
    if count < 5 {
        fmt.Println("count =", count)
        goto loop
    }
    fmt.Println("循环结束, count =", count)
}
```

> **建议**：`goto` 会使代码流程难以追踪，绝大多数情况下可以用 `for`、`break`、`continue` 替代。仅在错误处理等少数场景中可能有用。

## 综合示例

下面是一个综合运用控制流的小程序：

```go
package main

import (
    "fmt"
    "math/rand"
)

func main() {
    target := rand.Intn(100) + 1

    fmt.Println("我想了一个 1-100 之间的数字，猜猜看！")

    for attempt := 1; attempt <= 7; attempt++ {
        var guess int
        fmt.Printf("第 %d 次猜测: ", attempt)
        fmt.Scanf("%d", &guess)

        switch {
        case guess < target:
            fmt.Println("太小了！")
        case guess > target:
            fmt.Println("太大了！")
        default:
            fmt.Printf("恭喜你！答案就是 %d\n", target)
            return
        }
    }
    fmt.Printf("游戏结束！答案是 %d\n", target)
}
```

## 总结

本篇详细介绍了 Go 的控制流语句。`if` 语句支持带初始化语句的独特语法；`for` 是 Go 唯一的循环关键字，通过不同写法可以实现经典循环、条件循环和无限循环；`range` 提供了优雅的遍历方式；`switch` 无需 `break` 且支持多值匹配和 `fallthrough` 穿透；`break` 和 `continue` 配合标签可以精确控制嵌套循环的跳转。Go 的控制流设计简洁而实用，去除了许多传统语言中的冗余语法。

