Go 零基础教程

Go 循环控制

在 Go 语言中,breakcontinue 语句允许你打破常规的循环条件,提前跳出循环或者直接跳过某些特定的迭代步骤。

相比于只能干巴巴地等待循环条件变成 false,灵活运用这两个语句能够让你的代码逻辑更加紧凑、执行效率更高。

本章我们将深入探讨它们的机制,并学习如何利用 Label(标签)来解决复杂的嵌套循环控制问题。

1. 深入理解 break 语句

break 语句的作用极其干脆:它会立即终止最内层的 forswitchselect 语句的执行。当程序遇到 break 时,它会瞬间跳出当前的代码块,并继续执行循环(或 switch/select)结构后面的下一行代码。

1.1 在 for 循环中的基础用法

break 最经典的用法是在遍历数据时,一旦找到了目标,就立刻停止无意义的后续查找。

package main

import "fmt"

func main() {
	numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	target := 5
	
	for i, num := range numbers {
		if num == target {
			fmt.Printf("在索引 %d 处找到了目标值 %d\n", i, target)
			break // 目标已找到,立刻终止整个循环!
		}
		fmt.Printf("正在检查数字: %d\n", num)
	}
	fmt.Println("循环彻底结束")
}

运行结果:

正在检查数字: 1
正在检查数字: 2
正在检查数字: 3
正在检查数字: 4
在索引 4 处找到了目标值 5
循环彻底结束

你看,程序在检查到 5 之后就 break 了,剩下的 6 到 10 根本没有被遍历,这极大地节省了计算资源。

1.2 breakswitch 中的陷阱

在上一章我们讲过,Go 语言的 switch 默认是不需要 break 的,匹配成功后会自动退出。
但是,如果你把 switch 嵌套在一个 for 循环里面,并且在 switchcase 里写了一个 break请注意:这个 break 只会跳出当前的 switch,而不会跳出外面的 for 循环!

package main

import "fmt"

func main() {
	for i := 0; i < 3; i++ {
		switch i {
		case 1:
			fmt.Println("遇到 1,执行 break")
			break // 警告:这里只跳出了 switch,for 循环依然会继续!
		default:
			fmt.Println("当前数字:", i)
		}
		fmt.Println("--- 一次 for 循环迭代结束 ---")
	}
}

如果你的本意是在 switch 里触发某个条件后,把外面的 for 循环也一起停掉,你就必须使用带标签的 break

1.3 高阶技巧:使用标签 (Label) 的 break

当面对多层嵌套循环时,普通的 break 只能跳出最内层的那一个循环。为了能从内层直接“精准狙击”并跳出指定的外层循环,Go 引入了标签 (Label) 机制。

package main

import "fmt"

func main() {
OuterLoop: // 1. 定义一个名为 OuterLoop 的标签,放在外层 for 循环的正上方
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			fmt.Printf("i=%d, j=%d\n", i, j)
			
			if i == 1 && j == 1 {
				fmt.Println("条件满足,直接轰炸并跳出外层循环!")
				break OuterLoop // 2. 指定跳出 OuterLoop 标签所代表的那个循环
			}
		}
	}
	fmt.Println("外层循环已结束")
}

i=1j=1 时,break OuterLoop 会直接把外层的 for i 循环干掉,程序直接跳到最后一行打印结束语。

2. 深入理解 continue 语句

如果说 break 是“彻底不干了”,那么 continue 就是“这次不干了,直接下一把”。
当在循环中遇到 continue 时,程序会立刻放弃当前这一次迭代中剩余的代码,直接跳回循环的头部,开始下一次迭代评估。

2.1 在 for 循环中的基础用法

continue 非常适合用来“过滤”掉你不关心的数据。比如,只打印偶数:

package main

import "fmt"

func main() {
	numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	
	for _, num := range numbers {
		if num%2 != 0 { // 如果是奇数
			continue    // 忽略下方代码,直接去取下一个 num
		}
		fmt.Printf("发现偶数: %d\n", num)
	}
	fmt.Println("循环结束")
}

当遇到奇数时,continue 会被触发,导致后面的 fmt.Printf 被跳过,循环直接推进到下一个数字。

2.2 使用标签 (Label) 的 continue

break 一样,continue 也可以配合标签使用。它的作用是:中断当前内层循环的执行,并直接推进外层循环的下一次迭代

package main

import "fmt"

func main() {
OuterLoop:
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			if i == 1 && j == 1 {
				fmt.Printf("遇到 i=1, j=1,终止当前内循环,直接开始外循环的下一次迭代\n")
				continue OuterLoop // 放弃内层循环剩下的步骤,让外层的 i 变成 2
			}
			fmt.Printf("i=%d, j=%d\n", i, j)
		}
	}
	fmt.Println("外层循环结束")
}

3. 实战演示:数据清洗

来看一个综合应用 continue 的实际案例。假设你正在读取一批传感器数据,你需要过滤掉所有的负数(无效数据),只处理正数。

package main

import "fmt"

func main() {
	data := []int{10, -5, 20, -3, 30, -1}
	
	fmt.Println("开始处理有效数据:")
	for _, value := range data {
		if value <= 0 {
			// 遇到脏数据,用 continue 轻松跳过
			continue 
		}
		
		// 只有干净的数据才能走到这里
		fmt.Printf("正在处理有效值: %d\n", value)
	}
}