982 字
约 3 分钟
5
S09-B06 · 控制流:`for` 是唯一的循环
适用读者:从 C/Java/Python 转来,找不到
while的人。
前置:S09-B02。
实测环境:Go 1.22.2(2026-11-05)。
覆盖旧文:A-1458(28) A-1459(34) A-1460(48) A-1461(35) A-1462(19) A-1463(20) A-1464(17) = 201 点。
覆盖扩展:E-S09-007(range的副本语义)。
上一篇:S09-B05 · map 与 list:零值、随机遍历与删除
下一篇:S09-B07 · 函数与闭包。
1. 一句话结论
Go 只有一个循环关键字 for,但能表达三种形态;switch 默认不穿透(要穿透得写 fallthrough)。
| 你以为需要 | Go 的写法 |
|---|---|
while (cond) |
for cond { } |
while (true) |
for { } |
do { } while |
for { ...; if !cond { break } } |
for (init; cond; post) |
for init; cond; post { } |
2. 实测:switch 默认不穿透
switch x { // x = 2
case 1: fmt.Print("one ")
case 2: fmt.Print("two ")
fallthrough
case 3: fmt.Print("three ")
}
实测输出:two three
| 行为 | 说明 |
|---|---|
| 默认 | 命中一个 case 就结束(不穿透) |
fallthrough |
显式跳到下一个 case(不再判断条件) |
| 多值 case | case 1, 2: |
| 无表达式 switch | switch { case x > 0: }(相当于 if-else 链) |
| 类型 switch | switch v := i.(type) { case int: } |
"默认不穿透"省掉了 C 语言里最容易忘的 break,这就是"收敛选择"的又一个例子。
3. for range 的副本语义(E-S09-007)
type P struct{ X int }
ps := []P{{1}, {2}}
for _, p := range ps { p.X = 99 } // 改的是副本
fmt.Println(ps) // [{1} {2}] ← 没变
for i := range ps { ps[i].X = 99 } // 按下标改
fmt.Println(ps) // [{99} {99}]
| 写法 | 效果 |
|---|---|
for _, p := range ps |
p 是元素副本,改它无效 |
for i := range ps |
通过 ps[i] 修改有效 |
for i := range ps + &ps[i] |
指针修改有效 |
这是 Go 新手最高频的 bug 之一:以为 range 给的是引用。
例外:如果元素本身是"含指针的结构"(如 []*P),改 p.X 会生效——因为改的是指针指向的对象。
4. break / continue 与标签
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 { continue outer } // 跳到外层下一轮
if i == 2 { break outer } // 跳出外层
}
}
| 语句 | 作用 |
|---|---|
break |
跳出最内层 for/switch/select |
continue |
进入最内层下一轮 |
break label |
跳出指定层 |
goto label |
跳到同函数内的标签(不要跨层用) |
goto 的边界:不能跳到另一个函数、不能跳过变量声明。实践里几乎不用——它存在主要是给编译器/生成代码用。
5. 五个误区
| 误区 | 真相 |
|---|---|
"Go 有 while" |
没有;用 for cond 表达 |
"switch 会穿透" |
默认不穿透,要 fallthrough |
"range 给的是引用" |
是副本(除指针/引用类型元素) |
"break 能跳出两层" |
只能跳最内层,多层要标签 |
"goto 可以随意用" |
限制多、可读性差,实践不用 |
6. 自检
- 用
for写出while (i < n)的等价形式。 switch默认是否穿透?如何穿透?for _, p := range ps { p.X = 1 }为什么不生效?怎么改?break与break label的区别?- 无表达式
switch { case x>0: }等价于什么?
7. 下一篇
S09-B07 · 函数、闭包与"循环变量捕获"
控制流讲完,下一篇是最容易出"看起来对但结果错"的地方:闭包捕获循环变量。会给出实测的三种写法对比。
本篇属于系列《S09 · Go 语言》。旧文来源:A-1458–A-1464;扩展点 E-S09-007。switch 与 range 行为为 Go 1.22.2 实测。
S09-B06 · 控制流:`for` 是唯一的循环
https://www.clxhxhhr.top/posts/5304/ 评论
0 条
还没有评论,先写一条吧。