Go 循环:for、range、break、continue

很多语言都有 forwhiledo-while 三种循环,Go 却只保留了 for 一个关键字。别担心,它通过几种不同的写法,把上面三种循环的活儿全包了,语法反而更统一、更好记。本篇带你讲透 Go 循环的所有用法。

一、for 的三种形式

同一个 for,根据写法不同,可以扮演三种角色:

三种写法放一起对比最直观:

package main

import "fmt"

func main() {
    // 形式 1:经典 C 风格(初始化; 条件; 后置)
    sum := 0
    for i := 1; i <= 10; i++ {
        sum += i
    }
    fmt.Println("1 到 10 求和 =", sum) // 55

    // 形式 2:省略初始化和后置,相当于 while
    n := 1
    for n < 100 {
        n *= 2
    }
    fmt.Println("第一个 >= 100 的 2 的幂:", n) // 128

    // 形式 3:全部省略,无限循环(必须配 break 跳出)
    count := 0
    for {
        count++
        if count >= 3 {
            break
        }
    }
    fmt.Println("count =", count) // 3
}

同样别忘了一条 Go 的硬规矩:左花括号 { 必须和 for 写在同一行,不能另起一行。

二、for range:遍历集合

遍历数组、切片、字符串、map 时,Go 提供了更顺手的 for range 语法。它会依次取出每个元素,并附带它的索引(或 map 的)。当你只关心值、不关心索引时,记得用空白符 _ 把索引丢掉,否则编译器会报"声明了但未使用"。

package main

import "fmt"

func main() {
    nums := []int{10, 20, 30}

    // range 同时返回 索引 和 值
    for i, v := range nums {
        fmt.Printf("nums[%d] = %d\n", i, v)
    }

    // 只想要值,用 _ 丢掉索引
    total := 0
    for _, v := range nums {
        total += v
    }
    fmt.Println("总和:", total) // 60

    // 遍历 map,range 返回 键 和 值
    m := map[string]int{"a": 1, "b": 2}
    for k, v := range m {
        fmt.Println(k, "=", v)
    }
}

range 的返回值个数是固定的:数组和切片返回 (索引, 值),map 返回 (键, 值),字符串返回 (字节位置, rune)。只要第一个、不要第二个,也可以写成 for i := range nums

三、range 遍历字符串:按 rune 解码

这是个初学者非常容易踩坑的点。Go 字符串底层是 UTF-8 字节序列,直接用 len() 得到的是字节数而不是字符数,中文一个字占 3 字节。但用 range 遍历字符串时,Go 会自动按 rune(Unicode 码点)解码,每次取出一个真正的字符。

package main

import "fmt"

func main() {
    s := "Go语言"
    // len 返回字节数,UTF-8 下每个中文占 3 字节
    fmt.Println("字节数:", len(s)) // 8

    // range 字符串:自动按 rune(Unicode 码点)解码
    // i 是该字符的"字节起始位置",r 是一个 rune
    for i, r := range s {
        fmt.Printf("位置 %d: %c\n", i, r)
    }
}

所以处理含中文的字符串,优先用 for range 而不是按下标逐字节访问,否则容易切出"半个汉字"产生乱码。这也是 Go 在字符串处理上比很多语言更贴心的地方。

四、嵌套循环:九九乘法表

循环里套循环,常用于二维结构(矩阵、表格、坐标)。经典练手题——九九乘法表,用双层 for 就能写出来:

package main

import "fmt"

func main() {
    // 九九乘法表:双层 for 循环
    for i := 1; i <= 9; i++ {
        for j := 1; j <= i; j++ {
            fmt.Printf("%d*%d=%-2d ", j, i, i*j)
        }
        fmt.Println()
    }
}

这里用了一个格式化小技巧 %-2d ——负号表示左对齐,2 表示占两位,这样每列宽度一致,输出才整齐。

五、break、continue 与标签

break 用于提前结束循环,continue 用于跳过本次、直接进入下一轮。普通的 break 只能跳出最内层循环,如果你嵌套了好几层、想一次性全部跳出,就要给外层循环贴一个标签(label),然后写 break 标签名

package main

import "fmt"

func main() {
    // 用标签一次跳出多层循环
outer:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if i == 1 && j == 1 {
                break outer // 直接跳出外层,不再继续
            }
            fmt.Printf("i=%d j=%d\n", i, j)
        }
    }

    // continue:跳过本次循环体的剩余代码,直接进入下一轮
    for i := 0; i < 6; i++ {
        if i%2 == 0 {
            continue // 跳过偶数
        }
        fmt.Println("奇数:", i) // 1 3 5
    }
}

标签是一个以冒号结尾的标识符(比如 outer:),写在循环前面。continue 同样可以配合标签,跳到外层循环的下一轮。这种"带标签的跳转"作用范围严格限定在循环结构内,比裸 goto 安全得多,也不会到处乱跳。

小结

记住一句话:Go 只有 for,但通过三种形式 + range + 标签,足以覆盖所有循环需求。新手最容易忘的两点——range 字符串会按 rune 解码、break 配合标签可以跳出多层。下一篇我们来写 Go 的函数

上一篇 Go 条件语句 | 下一篇 Go 函数

✈️💬