第 3 章:流程控制与函数
jerry北京市2026年4月29日Go 5 次阅读 约 13 分钟

掌握 Go 的条件判断、循环、switch 语句,以及函数的定义、多返回值、defer 和闭包。
3.1 条件判断
if 语句
x := 10
if x > 0 {
fmt.Println("正数")
} else if x < 0 {
fmt.Println("负数")
} else {
fmt.Println("零")
}
Go 的 if 有一个独特特性——可以在条件前加一个初始化语句:
// err 的作用域仅限于 if-else 块内
if err := doSomething(); err != nil {
fmt.Println("出错了:", err)
return
}
这种写法在 Go 中非常常见,特别是错误处理场景。
3.2 循环
Go 只有 for 一种循环关键字,但它能覆盖所有循环场景:
// 标准 for 循环
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// 类似 while
n := 1
for n < 100 {
n *= 2
}
// 无限循环
for {
// 用 break 退出
break
}
// range 遍历
nums := []int{1, 2, 3}
for index, value := range nums {
fmt.Printf("索引:%d 值:%d\n", index, value)
}
// 只要值,不要索引
for _, v := range nums {
fmt.Println(v)
}
// 遍历 map
m := map[string]int{"a": 1, "b": 2}
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
// 遍历字符串(按 rune)
for i, ch := range "你好" {
fmt.Printf("%d: %c\n", i, ch)
}
break 和 continue 配合标签
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)
}
}
3.3 switch 语句
Go 的 switch 不需要 break,匹配到一个 case 后自动终止:
day := "Monday"
switch day {
case "Monday":
fmt.Println("周一")
case "Friday":
fmt.Println("周五")
default:
fmt.Println("其他")
}
// 多值匹配
switch day {
case "Saturday", "Sunday":
fmt.Println("周末")
default:
fmt.Println("工作日")
}
// 无条件 switch(替代 if-else 链)
score := 85
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 80:
fmt.Println("良好")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
如果需要穿透到下一个 case,使用 fallthrough:
switch 1 {
case 1:
fmt.Println("1")
fallthrough
case 2:
fmt.Println("2") // 也会执行
}
3.4 函数定义
// 基本函数
func add(a int, b int) int {
return a + b
}
// 参数类型相同可以简写
func add(a, b int) int {
return a + b
}
// 多返回值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为零")
}
return a / b, nil
}
result, err := divide(10, 3)
if err != nil {
fmt.Println(err)
}
命名返回值
func swap(a, b int) (x, y int) {
x = b
y = a
return // 裸 return,自动返回 x 和 y
}
命名返回值在短函数中可以提高可读性,但在长函数中建议显式 return。
可变参数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
// 展开切片传入
nums := []int{1, 2, 3}
fmt.Println(sum(nums...)) // 6
3.5 函数是一等公民
Go 中函数可以赋值给变量、作为参数传递、作为返回值:
// 函数赋值给变量
add := func(a, b int) int {
return a + b
}
fmt.Println(add(1, 2))
// 函数作为参数
func apply(a, b int, op func(int, int) int) int {
return op(a, b)
}
result := apply(3, 4, func(a, b int) int {
return a * b
})
fmt.Println(result) // 12
3.6 闭包(Closure)
闭包是引用了外部变量的函数:
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
闭包的关键点:内部函数持有外部变量的引用(不是拷贝),所以每次调用都能修改同一个变量。
闭包陷阱
// 经典陷阱:循环变量捕获
funcs := make([]func(), 3)
for i := 0; i < 3; i++ {
funcs[i] = func() {
fmt.Println(i) // 捕获的是变量 i 的引用
}
}
funcs[0]() // 3(不是 0)
funcs[1]() // 3
funcs[2]() // 3
// 解决方案:通过参数传值
for i := 0; i < 3; i++ {
i := i // 创建新的局部变量(Go 1.22+ 循环变量已修复此问题)
funcs[i] = func() {
fmt.Println(i)
}
}
3.7 defer
defer 延迟执行函数调用,在当前函数返回前执行。多个 defer 按 LIFO(后进先出)顺序执行:
func main() {
fmt.Println("开始")
defer fmt.Println("延迟 1")
defer fmt.Println("延迟 2")
defer fmt.Println("延迟 3")
fmt.Println("结束")
}
// 输出:
// 开始
// 结束
// 延迟 3
// 延迟 2
// 延迟 1
常见用途——资源清理:
func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // 确保函数返回前关闭文件
// 读取文件内容...
return nil
}
defer 的求值时机
func main() {
x := 10
defer fmt.Println(x) // x 在 defer 时就被求值了,值为 10
x = 20
}
// 输出:10(不是 20)
3.8 init 函数
每个包可以有一个或多个 init 函数,在 main 函数之前自动执行:
var config map[string]string
func init() {
config = make(map[string]string)
config["env"] = "production"
fmt.Println("初始化完成")
}
func main() {
fmt.Println(config["env"])
}
// 输出:
// 初始化完成
// production
执行顺序:全局变量初始化 → init() → main()
3.9 面试要点
-
Go 为什么只有
for循环?- 简化语言设计,一个关键字覆盖所有循环场景
-
defer的执行顺序和求值时机?- LIFO 顺序执行,参数在 defer 语句处立即求值
-
闭包捕获变量是值还是引用?
- 引用。闭包持有外部变量的引用,修改会影响原变量
-
switch和其他语言有什么不同?- 不需要 break,自动终止
- 支持无条件 switch
- 需要穿透时用
fallthrough
练习
- 编写一个函数,接收一个整数切片,返回其中的最大值和最小值
- 使用闭包实现一个斐波那契数列生成器
- 编写一个函数,使用 defer 记录函数的执行时间
- 使用无条件 switch 实现一个简单的成绩等级判断
评论
登录 后发表评论
暂无评论