4、流程控制:if-else条件判断、for循环(三种形式)、switch语句、goto、break、continue
流程控制,说白了就是让代码「走哪条路」或者「走几遍」。我见过不少新手,一上来就写几百行的线性代码,逻辑全堆在一起,最后自己都看不懂。其实用好流程控制,代码会清晰很多。
Go 语言的流程控制关键字不多,但每个都很实用。咱们一个一个来看。
4.1 if-else 条件判断
Go 的 if 语句有个特点——条件表达式不需要括号。这跟 C 语言、Java 都不一样。我个人习惯是直接写,不加括号,干净利落。
package main
import "fmt"
func main() {
score := 85
if score >= 90 {
fmt.Println("优秀")
} else if score >= 80 {
fmt.Println("良好")
} else if score >= 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
}
这里有个小细节:else if 必须跟前面的右花括号在同一行。你不能写成:
if score >= 90 {
// ...
}
else { // 编译错误!
// ...
}
嗯,Go 的编译器就是这么「强迫症」。不过习惯了就好,其实这样写出来的代码更整齐。
if err := doSomething(); err != nil {
fmt.Println("出错了:", err)
}
这个写法在 Go 里非常常见,尤其是处理错误时。变量 err 的作用域只在 if-else 块内,不会污染外部。
我在项目中遇到过一个问题:有人把 if err == nil 和 if err != nil 混着用,导致逻辑混乱。我的建议是——优先处理错误情况。也就是先写 if err != nil { return },把正常逻辑放在后面。这样代码的嵌套层次会少很多。
4.2 for 循环(三种形式)
Go 语言只有 for 这一个循环关键字。没有 while,没有 do-while。但别担心,一个 for 能搞定所有场景。它有三种形式。
形式一:标准 for 循环
跟 C 语言很像,但同样没有括号。
for i := 0; i < 5; i++ {
fmt.Println("第", i, "次循环")
}
这里 i := 0 是初始化语句,i < 5 是条件,i++ 是后置语句。三个部分用分号隔开。
形式二:类似 while 的循环
把初始化语句和后置语句都省略,就变成了 while 循环。
sum := 0
for sum < 100 {
sum += 10
}
fmt.Println(sum) // 输出 100
说白了,这就是 Go 里的 while。你想想看,少学一个关键字,是不是挺省心的?
形式三:无限循环
连条件都不写,就是无限循环。
for {
fmt.Println("停不下来...")
// 通常配合 break 使用
}
我曾经在写一个后台服务时,就用无限循环加 time.Sleep 来做定时任务。当然,记得加退出条件,不然程序真的停不下来。
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i) // 可能输出 3,3,3
}()
}
从 Go 1.22 开始,每次迭代都会创建新的变量。如果你还在用旧版本,记得在 goroutine 里把 i 作为参数传进去。
4.3 switch 语句
Go 的 switch 比很多语言都灵活。它不需要在每个 case 后面写 break,默认只会执行匹配的那个 case。这一点跟 C 语言完全不同。
day := 3
switch day {
case 1:
fmt.Println("星期一")
case 2:
fmt.Println("星期二")
case 3:
fmt.Println("星期三")
case 4:
fmt.Println("星期四")
case 5:
fmt.Println("星期五")
default:
fmt.Println("周末")
}
如果你想「穿透」到下一个 case,可以用 fallthrough 关键字。不过我个人很少用,因为容易让逻辑变得混乱。
switch 还有一种更灵活的用法——不带表达式。这时候每个 case 就是一个条件判断。
score := 85
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 80:
fmt.Println("良好")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
你看,这跟 if-else 链效果一样,但看起来更整齐。我建议在条件比较多的时候用 switch,代码可读性会好很多。
switch score := getScore(); {
case score >= 90:
// ...
}
这样 score 的作用域就限制在 switch 块内了。
4.4 goto 语句
goto 在不少语言里被「妖魔化」了。其实它没那么可怕。Go 保留了 goto,但限制了它的使用——不能跳转到另一个函数,也不能跳过一个变量声明。
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if i*j > 10 {
goto exit
}
fmt.Println(i, j)
}
}
exit:
fmt.Println("跳出双层循环")
我曾经在解析复杂配置文件时用过 goto。当时需要从多层嵌套循环中直接跳出,用 break 要加标签,用 goto 反而更直观。当然,日常开发中我很少用 goto。你想想看,能用 break 或 return 解决的问题,就别用 goto。
goto skip
x := 10 // 编译错误:跳过了 x 的声明
skip:
fmt.Println(x)
4.5 break 和 continue
这两个关键字在循环里很常用。break 跳出当前循环,continue 跳过本次循环的剩余部分,进入下一次迭代。
for i := 0; i < 10; i++ {
if i == 3 {
continue // 跳过 3
}
if i == 7 {
break // 到 7 就停止
}
fmt.Println(i)
}
// 输出:0 1 2 4 5 6
在嵌套循环中,break 和 continue 默认只作用于最内层的循环。如果你想跳出外层循环,需要配合标签使用。
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer // 跳出外层循环
}
fmt.Printf("(%d,%d) ", i, j)
}
}
// 输出:(0,0) (0,1) (0,2) (1,0)
我记得刚学 Go 的时候,总觉得标签有点多余。后来在写一个矩阵运算的程序时,三层循环嵌套,用标签配合 break 一下子就搞定了。嗯,有些东西用到了才知道它的好。
- if-else:条件判断,优先处理错误情况
- for:三种形式,覆盖所有循环场景
- switch:比 if-else 更清晰,默认不穿透
- goto:慎用,适合跳出多层循环
- break/continue:控制循环流程,配合标签使用更强大
流程控制是编程的基础。说白了,代码就是「顺序、选择、循环」这三种结构的组合。把这些掌握好,你就能写出逻辑清晰、可读性强的 Go 代码了。