首页 / Go 语言入门教程 / for 循环

Go 语言入门教程

for 循环

本教程共 80 篇 · 第 19 篇 · 更新于 2026-07-27 · 约 7 分钟阅读

GoGo 入门教程forfor-range循环流程控制Go for

19. for 循环

本节目标:掌握 Go for 循环的三种形式,学会 for-range 遍历各种集合,理解为什么 Go 只保留了一个循环关键字。

Go 只有 for

Go 没有while、do-while,只有 for。但 for 能覆盖所有循环场景,设计上更简洁。

三种 for 形式

形式一:C 风格三段式

for init; condition; post {
    // 循环体
}
  • init:循环开始前执行一次
  • condition:每次迭代前判断,为 false 就退出
  • post:每次迭代后执行
for i := 0; i < 5; i++ {
    fmt.Println(i)
}
// 输出:0 1 2 3 4

三个部分都可以省略,但分号不能省(除非只剩条件)。

形式二:类似 while

省略 init 和 post,只保留条件:

n := 1
for n < 100 {
    n *= 2
}
fmt.Println(n)  // 128

这就是其他语言的 while (n < 100)

形式三:无限循环

全部省略就是死循环:

for {
    fmt.Println("永远运行")
}

需要用 break 跳出:

count := 0
for {
    count++
    if count >= 10 {
        break
    }
}
fmt.Println(count)  // 10

多变量 for

for 可以同时初始化和操作多个变量:

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

输出:

0 10
1 9
2 8
3 7
4 6

for-range 遍历

for-range 是 Go 里遍历集合最常用的方式:

for index, value := range collection {
    // 使用 index 和 value
}

遍历切片

nums := []int{10, 20, 30, 40, 50}
for index, value := range nums {
    fmt.Printf("索引:%d,值:%d\n", index, value)
}

输出:

索引:0,值:10
索引:1,值:20
索引:2,值:30
索引:3,值:40
索引:4,值:50

遍历数组

数组的遍历和切片一样:

arr := [3]string{"Go", "Python", "Rust"}
for i, v := range arr {
    fmt.Printf("%d: %s\n", i, v)
}

遍历字符串

for-range 遍历字符串时,按 Unicode 字符(rune)遍历:

s := "Hello,世界"
for i, r := range s {
    fmt.Printf("位置:%d,字符:%c\n", i, r)
}

输出:

位置:0,字符:H
位置:1,字符:e
...
位置:6,字符:世
位置:9,字符:界

注意位置不连续,因为中文字符占 3 字节。

遍历 map

m := map[string]int{
    "Go":     2009,
    "Python": 1991,
    "Rust":   2010,
}
for key, value := range m {
    fmt.Printf("%s: %d\n", key, value)
}
Note

map 的遍历顺序是随机的,每次运行可能不同。这是 Go 刻意的设计,防止你依赖遍历顺序。

省略索引或值

只需要值,不需要索引:

for _, value := range nums {
    fmt.Println(value)
}

只需要索引,不需要值:

for index := range nums {
    fmt.Println(index)
}

两个都不需要(只循环次数):

for range nums {
    fmt.Println("执行一次")
}
Tip

不需要的变量用 _ 丢弃,这是 Go 的惯例。不要声明了不用,编译器会报错。

嵌套循环

循环可以嵌套:

for i := 1; i <= 9; i++ {
    for j := 1; j <= i; j++ {
        fmt.Printf("%d*%d=%-4d", j, i, i*j)
    }
    fmt.Println()
}

输出九九乘法表:

1*1=1
1*2=2   2*2=4
1*3=3   2*3=6   3*3=9
...

break 和 continue

break

break 跳出当前循环:

for i := 0; i < 10; i++ {
    if i == 5 {
        break  // i 等于 5 时退出循环
    }
    fmt.Println(i)
}
// 输出:0 1 2 3 4

continue

continue 跳过本次迭代,进入下一次:

for i := 0; i < 10; i++ {
    if i%2 == 0 {
        continue  // 跳过偶数
    }
    fmt.Println(i)
}
// 输出:1 3 5 7 9
Note

breakcontinue 只对最内层循环生效。嵌套循环里,内层的 break 不会退出外层循环。要退出外层需要用标签,下一节讲。

实际示例

累加求和

sum := 0
for i := 1; i <= 100; i++ {
    sum += i
}
fmt.Println(sum)  // 5050

查找元素

nums := []int{5, 3, 8, 1, 9, 2}
target := 8

for i, v := range nums {
    if v == target {
        fmt.Printf("找到 %d,在位置 %d\n", v, i)
        break
    }
}

过滤数据

nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var evens []int

for _, v := range nums {
    if v%2 != 0 {
        continue  // 跳过奇数
    }
    evens = append(evens, v)
}
fmt.Println(evens)  // [2 4 6 8 10]

模拟 while 读取输入

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    line := scanner.Text()
    if line == "quit" {
        break
    }
    fmt.Println("你输入了:", line)
}

循环变量的作用域

在 Go 1.22 之前,for 循环的变量在整个循环中只创建一次,每次迭代复用同一个变量。这导致一个经典的闭包陷阱。

Go 1.22 开始,每次迭代都创建新的变量:

// Go 1.22+ 每次迭代 i 是新变量
for i := 0; i < 3; i++ {
    go func() {
        fmt.Println(i)  // 每个 goroutine 打印不同的值
    }()
}

这个变化很重要,下一节会详细讲。

常见问题

死循环忘了 break

for {
    // 如果没有 break 或 return,永远停不下来
}

无限循环一定要确保有退出条件。

遍历 map 依赖顺序

// 不要这样做!map 遍历顺序是随机的
for k, v := range m {
    results = append(results, k)
}

如果需要有序遍历,先把 key 取出来排序:

keys := make([]string, 0, len(m))
for k := range m {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, m[k])
}

修改遍历中的切片

for-range 遍历的是切片的副本,修改 value 不影响原切片:

nums := []int{1, 2, 3}
for _, v := range nums {
    v *= 2  // 不影响 nums
}
fmt.Println(nums)  // [1 2 3]

要修改原切片,用索引:

for i := range nums {
    nums[i] *= 2
}
fmt.Println(nums)  // [2 4 6]

学完这节你能做什么

  • 用三种形式的 for 循环
  • 用 for-range 遍历切片、数组、字符串、map
  • 省略不需要的索引或值
  • 用 break 和 continue 控制循环
  • 避免遍历中修改切片的陷阱

下一节讲 range 的新遍历方式(Go 1.22 range over int 和 1.23 range over func)。