Go语言的 for 循环详解

for 循环是Go语言中唯一的循环语句,非常灵活,可以用于实现各种循环控制结构。下面详细介绍其用法和一些注意事项。

1. 基本的 for 循环写法

基本的 for 循环语法与C语言类似,结构如下:

for 初始化语句; 条件表达式; 后置语句 {
    // 循环体
}

示例:

for i := 0; i < 10; i++ {
    fmt.Println(i)
}

这个循环从0打印到9。

2. 使用 rangefor 循环

range 用于遍历数组、切片、字符串、map 以及通道。用 range 遍历时,会返回两个值:第一个是索引(或键),第二个是元素(或值)。

示例:

遍历数组或切片:

nums := []int{2, 3, 4}
for i, num := range nums {
    fmt.Println(i, num)
}

遍历字符串:

str := "hello"
for i, ch := range str {
    fmt.Println(i, string(ch))
}

遍历 map:

m := map[string]string{"a": "apple", "b": "banana"}
for k, v := range m {
    fmt.Println(k, v)
}

遍历通道:

ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)
for val := range ch {
    fmt.Println(val)
}

3. Go 1.20 以前的 for 循环中使用协程的问题

在Go 1.20之前,如果在 for 循环中使用协程(goroutine),容易出现变量捕获的问题。示例:

for i := 0; i < 10; i++ {
    go func() {
        fmt.Println(i)
    }()
}

这段代码输出的结果可能不是预期的0到9,而是多个相同的值。原因在于闭包函数中捕获了 i 变量,而不是其当前值。正确的做法是传递 i 的副本:

for i := 0; i < 10; i++ {
    go func(i int) {
        fmt.Println(i)
    }(i)
}

这样可以确保每个协程都使用不同的 i 值。

4. for 循环中的 defer 使用

defer 语句用于在函数返回之前执行某些操作。在 for 循环中使用 defer 需要注意,所有 defer 语句会在循环结束后,按照后进先出的顺序执行。

示例:

for i := 0; i < 3; i++ {
    defer fmt.Println(i)
}

输出顺序是:

2
1
0

5. 其他常见用法和注意事项

  • 无限循环:省略所有三部分,形成无限循环。

    for {
        // 无限循环体
    }
    
  • 单条件循环:省略初始化语句和后置语句。

    i := 0
    for i < 10 {
        fmt.Println(i)
        i++
    }
    
  • 循环中的 breakcontinuebreak 退出循环,continue 跳过当前迭代。

    for i := 0; i < 10; i++ {
        if i == 5 {
            break
        }
        if i%2 == 0 {
            continue
        }
        fmt.Println(i)
    }
    
  • 标签和多重循环:使用标签可以跳出多重循环。

    outer:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if i == 1 {
                break outer
            }
            fmt.Println(i, j)
        }
    }
    

这些是 Go 语言中 for 循环的常见用法和注意事项,希望对你有所帮助。