1.3 控制流
if/else 条件判断
基本用法
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("不及格")
}
}
带初始化语句的 if(Go 特色)
Go 的 if 可以在条件前执行一个简短语句,变量作用域仅限于 if/else 块内:
func main() {
// err 只在 if/else 块内可见
if err := doSomething(); err != nil {
fmt.Println("出错了:", err)
} else {
fmt.Println("成功")
}
// fmt.Println(err) // ❌ 编译错误:err 在这里不可见
}
func doSomething() error {
return nil
}
这个模式在 Go 中非常常见,特别是用于错误处理。
for 循环
Go 只有 for 一个循环关键字,但它可以替代其他语言中的 while 和 do-while。
经典三段式 for
func main() {
// 类似 C/Java 的 for
for i := 0; i < 5; i++ {
fmt.Println(i) // 0, 1, 2, 3, 4
}
}
while 风格(只有条件)
func main() {
// 相当于 while
n := 1
for n <= 100 {
n *= 2
}
fmt.Println(n) // 128
}
无限循环
func main() {
count := 0
for {
count++
if count >= 5 {
break // 使用 break 跳出
}
fmt.Println(count)
}
}
for-range 遍历
range 用于遍历数组、切片、字符串、Map 和 Channel:
func main() {
// 遍历切片
fruits := []string{"苹果", "香蕉", "橘子"}
for index, fruit := range fruits {
fmt.Printf("索引 %d: %s\n", index, fruit)
}
// 只需要值,用 _ 忽略索引
for _, fruit := range fruits {
fmt.Println(fruit)
}
// 只需要索引
for i := range fruits {
fmt.Println(i)
}
// 遍历字符串(按 rune 遍历)
for i, ch := range "Hello, 世界" {
fmt.Printf("%d: %c\n", i, ch)
}
// 遍历 Map
ages := map[string]int{
"Alice": 25,
"Bob": 30,
}
for name, age := range ages {
fmt.Printf("%s: %d岁\n", name, age)
}
}
break 和 continue
func main() {
// continue 跳过当前迭代
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue // 跳过偶数
}
fmt.Println(i) // 1, 3, 5, 7, 9
}
// 带标签的 break(跳出外层循环)
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)
}
switch 语句
Go 的 switch 比 C/Java 更强大灵活。
基本 switch
func main() {
day := "Monday"
switch day {
case "Monday":
fmt.Println("星期一,工作日开始")
case "Friday":
fmt.Println("星期五,快放假了")
case "Saturday", "Sunday": // 多个值用逗号分隔
fmt.Println("周末,休息日")
default:
fmt.Println("普通工作日")
}
}
Go 的 switch 不需要 break!每个 case 自动 break。如果需要贯穿到下一个 case,使用 fallthrough。
无条件 switch(替代 if-else 链)
func main() {
score := 85
// switch 后面不写变量,相当于 if-else 链
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 80:
fmt.Println("良好")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
}
类型 switch
func checkType(val interface{}) {
switch v := val.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
case bool:
fmt.Printf("布尔值: %t\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
func main() {
checkType(42)
checkType("hello")
checkType(true)
checkType(3.14)
}
fallthrough
func main() {
n := 3
switch {
case n > 0:
fmt.Println("正数")
fallthrough // 强制执行下一个 case
case n > -5:
fmt.Println("大于 -5")
case n > -10:
fmt.Println("大于 -10")
}
// 输出:
// 正数
// 大于 -5
}
⚠️
fallthrough在实际开发中很少使用,多数情况下 Go 的自动 break 行为就是你想要的。
defer 延迟执行
defer 会将函数调用推迟到当前函数返回时执行。多个 defer 按 LIFO(后进先出) 顺序执行。
基本用法
func main() {
fmt.Println("开始")
defer fmt.Println("结束") // 最后执行
fmt.Println("进行中")
// 输出:
// 开始
// 进行中
// 结束
}
多个 defer(LIFO 顺序)
func main() {
fmt.Println("开始计数")
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
fmt.Println("计数结束")
// 输出:
// 开始计数
// 计数结束
// 4 3 2 1 0
}
defer 的经典用法:资源清理
import "os"
func readFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close() // 确保函数返回前关闭文件
// 读取文件内容...
// 无论后面的代码是否出错,file.Close() 都会被调用
return nil
}
defer 的注意事项
func main() {
// defer 的参数在声明时就已经确定(值拷贝)
x := 10
defer fmt.Println("defer 中的 x:", x) // 输出 10,不是 20
x = 20
fmt.Println("当前 x:", x)
// 输出:
// 当前 x: 20
// defer 中的 x: 10
}
如果需要在 defer 中使用最新的值,可以用闭包:
func main() {
x := 10
defer func() {
fmt.Println("defer 闭包中的 x:", x) // 输出 20
}()
x = 20
}
goto(了解即可)
Go 支持 goto,但在实际开发中极少使用:
func main() {
i := 0
loop:
if i < 5 {
fmt.Println(i)
i++
goto loop
}
}
小结
| 语法 | 要点 |
|---|---|
if | 可以带初始化语句,变量作用域仅在 if/else 块内 |
for | Go 唯一的循环关键字,可以模拟 while 和无限循环 |
range | 遍历数组/切片/Map/字符串/Channel |
switch | 不需要 break,可以无条件使用,支持类型 switch |
defer | LIFO 执行,常用于资源清理 |