- 示例工程
- 教程
【免费下载链接】learngo
❤️ 1000+ Hand-Crafted Go Examples, Exercises, and Quizzes. 🚀 Learn Go by fixing 1000+ tiny programs.
导读
在 Go 语言中,循环只有一种形态——for语句,但它通过灵活的组合可以表达 while、无限循环、遍历等几乎所有循环场景。本文以 learngo 仓库中 13-loops/questions/01-loops.md 的十道核心题目为骨架,结合 13-loops 目录下的可运行示例源码,系统讲解for的三要素语法、条件省略、break/continue的跳转语义,以及for range对切片和命令行参数的遍历技巧。读完本文,你将能准确预测任意for循环的打印结果,并能在实战中写出正确、简洁的循环代码。
一、Go 中唯一的循环语句:for
Go 语言刻意保持语法精简,循环语句只有一种:for。没有while、forever、until这类关键字。当需要"当型循环"或"无限循环"时,都是通过省略for的相应子句来实现的。
// 经典三要素形式:init; condition; post for i := 1; i <= 1000; i++ { sum += i }这一点可以在仓库的 13-loops/01-basics/main.go 中得到印证——用一段循环把 1 累加到 1000,替代了原来逐行手写的sum += 1、sum += 2等语句。
二、三要素形式:init、condition、post 的执行顺序
for的完整写法是:
for init; condition; post { // 循环体 }执行流程为:先执行一次init(循环变量初始化),然后判断condition,为真则执行循环体,再执行post(通常是自增/自减),接着回到条件判断,如此往复。
题目:for i := 3; i > 0; i--打印什么?
for i := 3; i > 0; i-- { fmt.Println(i) }正确答案是3 2 1。初始i = 3,条件i > 0成立,打印 3;执行i--后为 2,打印 2;再减为 1,打印 1;再减为 0 时条件不成立,循环结束。
注意:这里与 C 系语言的do...while不同,Go 的for是"先判断、后执行",所以i = 0时循环体根本不会执行。
三、省略 post 子句:把递增逻辑移入循环体
post子句是可以省略的。此时循环的推进逻辑需要由程序员自己在循环体内完成,否则容易写出死循环。
题目:for i := 3; i > 0; { i--; ... }打印什么?
for i := 3; i > 0; { i-- fmt.Println(i) }正确答案是2 1 0。关键在于i--被放进了循环体,且发生在fmt.Println(i)之前:第一次迭代时i先自减为 2 再打印,最后一次迭代时i从 1 自减为 0 再打印,随后条件i > 0不成立,循环结束。
这道题与上一题形成鲜明对比:post子句中的i--在打印之后执行,循环体内的i--在打印之前执行,顺序不同,输出完全不同。这是 Go 初学者最容易踩的坑之一。
四、无限循环与 break:手动控制退出
当for的三个子句全部省略时,就得到了无限循环:
for { // 永远循环,直到遇到 break 或 return }仓库 13-loops/02-break/main.go 正是这种写法,配合break在满足条件时退出:
var ( sum int i = 1 ) for { if i > 5 { break } sum += i fmt.Println(i, "-->", sum) i++ }break会立即终止最内层的for、switch或select语句,把控制权交给其后的第一条语句。
题目:for i := 3; ; { if i <= 0 { break }; ... }打印什么?
for i := 3; ; { if i <= 0 { break } i-- fmt.Println(i) }正确答案是2 1 0。这里省略了condition和post,只保留init,循环本身无限执行,靠循环体内的break终止:迭代序列为i=3→ 自减为 2 打印 → 自减为 1 打印 → 自减为 0 打印 → 下一次判断i <= 0成立,break退出。
简化题:for ; true; {}可以怎么简化?
for ; true ; { // ... }正确答案是直接写成:
for { // ... }因为condition恒为true时与省略该子句等价。这也是 Go 社区中"无限循环"的标准写法。其他候选答案(如for true、for true;、for ; true)都不是合法的简化形式——Go 语法要求for后要么跟init; condition; post,要么只跟一个条件表达式(或range子句),不能只跟一个裸布尔值再加大括号。
五、continue:跳过本次迭代,进入下一次
continue与break相反:它不终止循环,而是跳过当前迭代的剩余部分,直接进入下一次循环(执行post或重新判断条件)。
题目:for i := 2; i <= 9; i++配合continue打印什么?
for i := 2; i <= 9; i++ { if i % 3 != 0 { continue } fmt.Println(i) }正确答案是3 6 9。i从 2 递增到 9,每当i % 3 != 0时执行continue跳过打印,因此只有能被 3 整除的 3、6、9 被输出。
仓库 13-loops/03-continue/01-b-after/main.go 展示了continue的一个真实陷阱:在无限循环中跳过迭代前,必须先推进循环变量,否则continue会不断跳过i++,导致死循环:
for { if i > 10 { break } if i%2 != 0 { // 关键:先 i++ 再 continue,否则死循环 i++ continue } sum += i fmt.Println(i, "-->", sum) i++ }经验法则:在continue之前,务必确保循环变量已经被推进。
六、for range:遍历切片与命令行参数
for range是 Go 遍历集合的最常用方式,可以对切片、数组、字符串、map 和 channel 进行迭代。对切片而言,它每次迭代返回两个值:索引(index)和元素的拷贝(value)。
for i, v := range os.Args[1:] { fmt.Println(i+1, v) }题目 A:for i, v := range os.Args[1:]打印什么?
假设程序这样运行:
go run main.go go is awesome则os.Args[1:]是["go", "is", "awesome"],正确答案是:
1 go 2 is 3 awesome这里把os.Args切片后取第 1 个元素到末尾(跳过程序名),range返回的索引从 0 开始,所以用i+1把编号显示为 1、2、3。
题目 B:只用索引for i := range os.Args[1:]打印什么?
for i := range os.Args[1:] { fmt.Println(i+1) }正确答案是:
1 2 3注意:当只需要索引、不需要值时,range子句可以只写一个接收变量。
题目 C:只用值for _, v := range os.Args[1:]打印什么?
for _, v := range os.Args[1:] { fmt.Println(v) }正确答案是:
go is awesome这里用空白标识符_丢弃索引,只打印元素值。_是 Go 中处理"不需要的返回值"的标准手段——如果声明了i却不使用,编译器会报错。
题目 D:for range os.Args计数,打印什么?
var i int for range os.Args { i++ } fmt.Println(i)正确答案是4。os.Args包含程序名本身,共 4 个元素(main.go、go、is、awesome)。这道题揭示了for range的一个重要用途:用range来计数——当循环体不关心索引也不关心值时,可以连接收变量都省略。
仓库 13-loops/05-for-range/01-loop-over-slices/main.go 完整展示了三种遍历命令行参数的思路,并明确标注第三种(for _, v := range os.Args[1:],直接切片跳过程序名)是最佳方式:
// #1st way: 传统 for,从索引 1 开始 // for i := 1; i < len(os.Args); i++ { ... } // #2nd way: range 全量遍历,用 continue 跳过索引 0 // for i, v := range os.Args { // if i == 0 { continue } // ... // } // #3rd way (best): 切片 + range,只取需要的值 for _, v := range os.Args[1:] { fmt.Printf("%q\n", v) }七、仓库实战印证:嵌套循环与标签跳转
理解for的三种子句省略方式和range的取值规则之后,就可以阅读仓库中更复杂的循环应用。
嵌套循环:九九乘法表
13-loops/04-nested-loops-multiplication-table/main.go 用双重for打印乘法表,外层循环负责行,内层循环负责列:
for i := 0; i <= max; i++ { // 打印行头 fmt.Printf("%5d", i) // 打印单元格 for j := 0; j <= max; j++ { fmt.Printf("%5d", i*j) } fmt.Println() }嵌套循环中,裸break只能跳出内层循环;要跳出外层循环,就需要用到标签。
带标签的 break:跳出外层循环
13-loops/08-word-finder-labeled-break/main.go 在双层循环中给外层循环贴上标签queries,内层命中第一个匹配单词后,用break queries直接跳出两层循环:
queries: for _, q := range query { for i, w := range words { if q == w { fmt.Printf("#%-2d: %q\n", i+1, w) // 找到第一个匹配就退出整个双层循环 break queries } } }这里的关键点(也是 13-loops/questions/03-labeled-statements.md 中的知识点):
- 标签的作用域是整个函数体,而不是它所在的语句块——这也是
goto能在函数内任意跳转的原因; - 一个标签同一时刻只能标注一条语句;
continue只能配合循环标签使用,如果标签标注的是switch等非循环语句,编译会报错;- 无标签的
break只终止最近的一层语句(可能是switch),不会连带终止外层for。
另外,goto同样能实现循环内跳转,但语义更难跟踪,实际编码中应优先使用带标签的break/continue。
八、小结与自测
知识点速查表
| 语法形态 | 等价含义 | 典型场景 |
|---|---|---|
for init; condition; post {} | 完整三要素循环 | 固定次数迭代 |
for condition {} | 相当于while | 条件驱动循环 |
for {} | 无限循环 | 配合break/return退出 |
for i, v := range slice {} | 同时取索引与值 | 遍历集合 |
for i := range slice {} | 只取索引 | 需要下标时 |
for _, v := range slice {} | 只取值 | 仅处理元素 |
for range slice {} | 只计数 | 统计元素个数 |
break | 终止最近一层语句 | 提前退出循环/switch |
continue | 跳过本次迭代 | 过滤不需要的元素 |
break label/continue label | 跳转到指定标签 | 退出/跳过嵌套多层 |
十个判断要点
- Go 只有
for一种循环语句; for i := 3; i > 0; i--打印3 2 1——post在循环体之后执行;- 把
i--移入循环体后打印2 1 0——执行顺序决定结果; for i := 3; ; {}是无限循环,靠循环体内break退出,同样输出2 1 0;for i := 2; i <= 9; i++配合if i%3 != 0 { continue }只打印3 6 9;for ; true; {}应简化为for {};for i, v := range os.Args[1:]对go run main.go go is awesome输出1 go / 2 is / 3 awesome;for i := range os.Args[1:]只输出索引1 2 3;for _, v := range os.Args[1:]只输出值go / is / awesome;for range os.Args可用于计数,程序名 + 3 个参数共计数 4 次。
建议实战路径:先运行 13-loops/01-basics/main.go、13-loops/02-break/main.go 等基础示例验证输出,再尝试 13-loops/exercises 中的练习(如01-sum-the-numbers、04-only-evens、05-break-up),最后对照 13-loops/05-for-range/01-loop-over-slices/main.go 和 13-loops/08-word-finder-labeled-break/main.go 掌握实战中的遍历与标签跳转技巧。只有亲手运行并预测输出,才能真正内化for的执行顺序与break/continue的跳转语义。
- 示例工程
- 教程
【免费下载链接】learngo
❤️ 1000+ Hand-Crafted Go Examples, Exercises, and Quizzes. 🚀 Learn Go by fixing 1000+ tiny programs.
相关推荐
iloader Nightly构建指南:为什么以及如何尝鲜最新版侧载功能
iloader Nightly构建指南:为什么以及如何尝鲜最新版侧载功能 iloader 是一款用户友好的 iOS 侧载工具(sideloader),支持一键安
文档教程前端uni-app x UTS 语言循环语法全解析:for、for...of、for...in、while 与 break/continue 的多端一致性实践
uni app x UTS 语言循环语法全解析:for、for...of、for...in、while 与 break/continue 的多端一致性实践 导读
示例工程前端移动开发跨平台Claude Desktop Debian版备份与恢复:用户配置迁移指南
Claude Desktop Debian版备份与恢复:用户配置迁移指南 Claude Desktop Debian版是Linux用户运行Claude AI桌面
开发工具桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考