Go 循环:for、range、break、continue
很多语言都有 for、while、do-while 三种循环,Go 却只保留了 for 一个关键字。别担心,它通过几种不同的写法,把上面三种循环的活儿全包了,语法反而更统一、更好记。本篇带你讲透 Go 循环的所有用法。
一、for 的三种形式
同一个 for,根据写法不同,可以扮演三种角色:
- 经典 C 风格:
for 初始化; 条件; 后置 { } - 当 while 用:
for 条件 { }(省略前后两部分) - 无限循环:
for { }(全省略,靠break跳出)
三种写法放一起对比最直观:
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 的函数。