主题
第03章 面试题精选 —— 流程控制
★ 基础题
Q1:Go 的 for 循环和 C/Java 的 for 循环有什么不同?
- 没有括号:
for i := 0; i < 10; i++比 C 少了() - 没有 while/do-while:所有循环都用
for for range是 Go 独有的语法糖- 大括号必须紧跟同行:
for { ... }不能写成下一行
Q2:Go 的 switch 和 C 的 switch 有什么不同?
| C/Java | Go | |
|---|---|---|
| 默认行为 | 自动穿透(fall-through) | 不穿透 |
| 显式穿透 | 不写 break | 用 fallthrough |
| case 类型 | 整数/字符/枚举 | 任意类型,包括字符串 |
| 多值匹配 | 写多个 case | 一个 case 可写多个值 case 1, 3, 5: |
| 无条件 switch | ❌ | ✅ switch { case cond: ... } |
| 类型 switch | ❌ | ✅ switch x.(type) { case int: ... } |
Q3:以下代码输出什么?
go
for i := 0; i < 5; i++ {
if i == 3 { break }
if i == 1 { continue }
fmt.Println(i)
}答:0、2。
i=0打印i=1continue 跳过i=2打印i=3break 退出
Q4:用 for range 遍历切片时,索引和值的类型分别是什么?
go
nums := []int{10, 20, 30}
for i, v := range nums {
// i 是 int,v 是 nums 的元素类型 int
}- 索引
i永远是int - 值
v是切片元素类型
⚠️ 遍历 map 时索引是 key、值是 value,且顺序随机。
Q5:下面的代码有问题吗?
go
nums := []int{1, 2, 3}
for _, v := range nums {
v *= 2
}
fmt.Println(nums)答:nums 不会被修改,输出 [1 2 3]。
原因:v 是元素的副本,修改副本不影响原切片。
要修改原切片:
go
for i := range nums {
nums[i] *= 2
}★★ 进阶题
Q6:for range 在 Go 1.22 之前有什么著名的 bug?
go
funcs := []func(){}
for _, v := range []int{1, 2, 3} {
funcs = append(funcs, func() { fmt.Println(v) })
}
for _, f := range funcs {
f()
}Go 1.22 之前:输出 3 3 3(三个闭包共享同一个 v)
Go 1.22+:输出 1 2 3(每次循环 v 都是新变量)
这是 Go 多年来最大的"隐式坑",1.22 默认修复。
Q7:switch x.(type) 是什么?什么时候用?
类型 switch(type switch),用来判断接口变量的实际动态类型。
go
func describe(x interface{}) {
switch v := x.(type) {
case nil:
fmt.Println("nil")
case int, int32, int64:
fmt.Println("整数族", v)
case string:
fmt.Println("字符串", v)
default:
fmt.Printf("未知 %T\n", v)
}
}注意:case int, int32, int64: 这种多类型写法时,v 是接口类型,不是具体类型。
Q8:Go 中 if 后面能不能跟 ; 之外的 init 语句?
只能跟一个简单语句(SimpleStmt),常见的有:
- 短变量声明:
if a := f(); a > 0 { ... } - 表达式:
if g(); cond { ... }(很少用)
不能跟:var、for、循环语句、return 等。
Q9:写一段代码:判断一个数是不是素数。
go
func isPrime(n int) bool {
if n < 2 { return false }
for i := 2; i*i <= n; i++ {
if n%i == 0 { return false }
}
return true
}关键点:循环条件是 i*i <= n,不需要遍历到 n。
Q10:用 Go 写"打印九九乘法表"。
go
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d×%d=%-2d ", j, i, i*j)
}
fmt.Println()
}考察点:嵌套循环 + 格式化输出 + 控制对齐。
★★★ 深度题
Q11:Go 为什么没有三元运算符 ? :?
官方理由:保持语言简洁性和可读性。? : 嵌套起来很难读,Go 推荐显式的 if-else:
go
// Java: int max = a > b ? a : b;
// Go:
var max int
if a > b { max = a } else { max = b }Q12:循环变量的逃逸问题
go
for i := 0; i < 3; i++ {
go func() {
time.Sleep(100 * time.Millisecond)
fmt.Println(i) // Go 1.22 前打印 3 3 3
}()
}Go 1.21 及之前:3 个 goroutine 共享同一个 i,因为 i 在循环外只声明一次。
Go 1.22+:每次循环 i 都是新变量,输出 0 1 2(顺序不定)。
📌 速记口诀
"if 不要括号;for 一统天下;switch 不穿透;range 取副本"
🎯 自测练习
- 写一个程序:从 1 数到 100,但跳过所有含 7 的数字(如 7、17、27...、70-79)
- 写九九乘法表
- 用 switch 实现一个"猜大小"游戏的判断逻辑
- 用
for range遍历一个 map 并按 key 排序输出(提示:先把 key 收到 slice,再排序)
完成后进入第04章 🚀