1.3 golang 控制流

3 阅读4分钟

1.3 控制流

if/else 条件判断

基本用法

package main

import "fmt"

func main() {
    score := 85

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

带初始化语句的 if(Go 特色)

Go 的 if 可以在条件前执行一个简短语句,变量作用域仅限于 if/else 块内:

func main() {
    // err 只在 if/else 块内可见
    if err := doSomething(); err != nil {
        fmt.Println("出错了:", err)
    } else {
        fmt.Println("成功")
    }
    // fmt.Println(err) // ❌ 编译错误:err 在这里不可见
}

func doSomething() error {
    return nil
}

这个模式在 Go 中非常常见,特别是用于错误处理。

for 循环

Go 只有 for 一个循环关键字,但它可以替代其他语言中的 whiledo-while

经典三段式 for

func main() {
    // 类似 C/Java 的 for
    for i := 0; i < 5; i++ {
        fmt.Println(i) // 0, 1, 2, 3, 4
    }
}

while 风格(只有条件)

func main() {
    // 相当于 while
    n := 1
    for n <= 100 {
        n *= 2
    }
    fmt.Println(n) // 128
}

无限循环

func main() {
    count := 0
    for {
        count++
        if count >= 5 {
            break // 使用 break 跳出
        }
        fmt.Println(count)
    }
}

for-range 遍历

range 用于遍历数组、切片、字符串、Map 和 Channel:

func main() {
    // 遍历切片
    fruits := []string{"苹果", "香蕉", "橘子"}
    for index, fruit := range fruits {
        fmt.Printf("索引 %d: %s\n", index, fruit)
    }
    
    // 只需要值,用 _ 忽略索引
    for _, fruit := range fruits {
        fmt.Println(fruit)
    }
    
    // 只需要索引
    for i := range fruits {
        fmt.Println(i)
    }
    
    // 遍历字符串(按 rune 遍历)
    for i, ch := range "Hello, 世界" {
        fmt.Printf("%d: %c\n", i, ch)
    }
    
    // 遍历 Map
    ages := map[string]int{
        "Alice": 25,
        "Bob":   30,
    }
    for name, age := range ages {
        fmt.Printf("%s: %d岁\n", name, age)
    }
}

break 和 continue

func main() {
    // continue 跳过当前迭代
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            continue // 跳过偶数
        }
        fmt.Println(i) // 1, 3, 5, 7, 9
    }
    
    // 带标签的 break(跳出外层循环)
outer:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if i == 1 && j == 1 {
                break outer // 直接跳出外层循环
            }
            fmt.Printf("(%d, %d) ", i, j)
        }
    }
    // 输出: (0, 0) (0, 1) (0, 2) (1, 0)
}

switch 语句

Go 的 switch 比 C/Java 更强大灵活。

基本 switch

func main() {
    day := "Monday"
    
    switch day {
    case "Monday":
        fmt.Println("星期一,工作日开始")
    case "Friday":
        fmt.Println("星期五,快放假了")
    case "Saturday", "Sunday": // 多个值用逗号分隔
        fmt.Println("周末,休息日")
    default:
        fmt.Println("普通工作日")
    }
}

Go 的 switch 不需要 break!每个 case 自动 break。如果需要贯穿到下一个 case,使用 fallthrough

无条件 switch(替代 if-else 链)

func main() {
    score := 85
    
    // switch 后面不写变量,相当于 if-else 链
    switch {
    case score >= 90:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 60:
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }
}

类型 switch

func checkType(val interface{}) {
    switch v := val.(type) {
    case int:
        fmt.Printf("整数: %d\n", v)
    case string:
        fmt.Printf("字符串: %s\n", v)
    case bool:
        fmt.Printf("布尔值: %t\n", v)
    default:
        fmt.Printf("未知类型: %T\n", v)
    }
}

func main() {
    checkType(42)
    checkType("hello")
    checkType(true)
    checkType(3.14)
}

fallthrough

func main() {
    n := 3
    switch {
    case n > 0:
        fmt.Println("正数")
        fallthrough // 强制执行下一个 case
    case n > -5:
        fmt.Println("大于 -5")
    case n > -10:
        fmt.Println("大于 -10")
    }
    // 输出:
    // 正数
    // 大于 -5
}

⚠️ fallthrough 在实际开发中很少使用,多数情况下 Go 的自动 break 行为就是你想要的。

defer 延迟执行

defer 会将函数调用推迟到当前函数返回时执行。多个 defer 按 LIFO(后进先出) 顺序执行。

基本用法

func main() {
    fmt.Println("开始")
    defer fmt.Println("结束") // 最后执行
    fmt.Println("进行中")
    
    // 输出:
    // 开始
    // 进行中
    // 结束
}

多个 defer(LIFO 顺序)

func main() {
    fmt.Println("开始计数")
    for i := 0; i < 5; i++ {
        defer fmt.Printf("%d ", i)
    }
    fmt.Println("计数结束")
    
    // 输出:
    // 开始计数
    // 计数结束
    // 4 3 2 1 0
}

defer 的经典用法:资源清理

import "os"

func readFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close() // 确保函数返回前关闭文件
    
    // 读取文件内容...
    // 无论后面的代码是否出错,file.Close() 都会被调用
    return nil
}

defer 的注意事项

func main() {
    // defer 的参数在声明时就已经确定(值拷贝)
    x := 10
    defer fmt.Println("defer 中的 x:", x) // 输出 10,不是 20
    x = 20
    fmt.Println("当前 x:", x)
    
    // 输出:
    // 当前 x: 20
    // defer 中的 x: 10
}

如果需要在 defer 中使用最新的值,可以用闭包:

func main() {
    x := 10
    defer func() {
        fmt.Println("defer 闭包中的 x:", x) // 输出 20
    }()
    x = 20
}

goto(了解即可)

Go 支持 goto,但在实际开发中极少使用

func main() {
    i := 0
loop:
    if i < 5 {
        fmt.Println(i)
        i++
        goto loop
    }
}

小结

语法要点
if可以带初始化语句,变量作用域仅在 if/else 块内
forGo 唯一的循环关键字,可以模拟 while 和无限循环
range遍历数组/切片/Map/字符串/Channel
switch不需要 break,可以无条件使用,支持类型 switch
deferLIFO 执行,常用于资源清理