news 2026/10/1 8:03:29

掌握 Go 的 for 循环:从基础语法到 for-range 遍历与 break/continue 控制流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
掌握 Go 的 for 循环:从基础语法到 for-range 遍历与 break/continue 控制流
  • 示例工程
  • 教程

【免费下载链接】learngo

❤️ 1000+ Hand-Crafted Go Examples, Exercises, and Quizzes. 🚀 Learn Go by fixing 1000+ tiny programs.

项目地址:https://gitcode.com/gh_mirrors/le/learngo
点击查看免费下载

导读

在 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跳转到指定标签退出/跳过嵌套多层

十个判断要点

  1. Go 只有for一种循环语句;
  2. for i := 3; i > 0; i--打印3 2 1——post在循环体之后执行;
  3. 把i--移入循环体后打印2 1 0——执行顺序决定结果;
  4. for i := 3; ; {}是无限循环,靠循环体内break退出,同样输出2 1 0;
  5. for i := 2; i <= 9; i++配合if i%3 != 0 { continue }只打印3 6 9;
  6. for ; true; {}应简化为for {};
  7. for i, v := range os.Args[1:]对go run main.go go is awesome输出1 go / 2 is / 3 awesome;
  8. for i := range os.Args[1:]只输出索引1 2 3;
  9. for _, v := range os.Args[1:]只输出值go / is / awesome;
  10. 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.

项目地址:https://gitcode.com/gh_mirrors/le/learngo
点击查看免费下载
上一篇:Claude Security 插件的 CWE 第三方数据声明:CWE-1003 视图、MITRE 许可条款与代码落地解析
下一篇:QuestDB 修复 PR 评审意见:fix-pr 技能完整实践指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 8:01:13

2026合肥注册公司代办机构选择宝典,新手创业都需要注意什么?

合肥创业注册代办市场观察这两年在合肥创业开公司的人越来越多&#xff0c;找代办注册几乎是大部分创业者的必经之路。核名、交材料、跑窗口、领执照、刻章、税务登记这一串事&#xff0c;没跑过两趟根本摸不清门路。很多第一次创业的人图省事&#xff0c;网上搜一圈就下单&…

作者头像 李华
网站建设 2026/10/1 8:00:41

企业软件定制开发能不能边用边改?系统迭代与安全发布方法

企业软件定制开发上线后&#xff0c;可以在不停止日常使用的情况下继续开发新功能&#xff0c;但不建议直接在正式系统中边操作边修改。较为安全的方式是&#xff1a;用户继续使用当前稳定版本&#xff0c;开发团队在独立环境中完成需求分析、开发和测试&#xff0c;再通过计划…

作者头像 李华
网站建设 2026/10/1 8:00:38

内景 复古侦探书房室内场景

本项目为前几天收费帮学妹做的一个项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 复古侦探书房室内场景 地址&#xff1a;本地PC端运行&#xff08;或WebGL端部署链接…

作者头像 李华
网站建设 2026/10/1 8:00:35

24小时自助健身房软硬件解决方案:从架构到部署实战指南

24小时自助健身房软硬件解决方案&#xff1a;从架构到部署实战指南 一、核心架构与技术选型 —— 搭建高可用自助健身系统 24小时自助健身房软硬件解决方案的核心&#xff0c;是构建一个支持多端联动、实时计费、智能门禁与会员自助管理的低耦合系统。根据项目建设经验&#xf…

作者头像 李华
网站建设 2026/10/1 8:00:35

让 Agent 点界面,比补接口贵 30 倍:computer use 成本实测

摘要&#xff1a; 本以为"让 AI 自己点界面"最省事&#xff0c;结果用本地沙箱实测&#xff1a;同一个导出报表任务&#xff0c;computer use 要滚 45 轮&#xff0c;成本比"补一个只读接口 两次调用"贵 30 倍。根因就两条——读数据也要先看图、等待只能…

作者头像 李华