Go for 循环详解

Go 编程实战系列(十九)

上一篇《Go switch 详解》中,我们学习了如何使用 switch 根据不同条件选择不同的代码分支。本篇继续学习 Go 流程控制中另一个非常重要的知识点:for 循环。

在实际开发中,经常需要让一段代码重复执行。例如:

复制代码
输出 1~100
遍历用户列表
处理一批文件
读取数据库数据
遍历字符串
统计订单金额
重复执行某个任务

如果把相同代码写几十遍,不仅麻烦,而且难以维护。因此编程语言提供了循环结构。

Go 的循环设计非常简洁,只有一个核心关键字:

复制代码
for

Go 没有单独的 whiledo while,但通过不同形式的 for,可以实现传统 forwhile、无限循环以及集合遍历等功能。

一、最基本的 for 循环

Go 中最经典的 for 写法是:

ini 复制代码
for 初始化; 条件; 更新 {
	循环代码
}

例如输出 1~5:

css 复制代码
package main

import "fmt"

func main() {
	for i := 1; i <= 5; i++ {
		fmt.Println(i)
	}
}

输出:

复制代码
1
2
3
4
5

这一段:

css 复制代码
for i := 1; i <= 5; i++

可以分成三个部分:

css 复制代码
i := 1
i <= 5
i++

分别表示:

复制代码
初始化
循环条件
循环后的更新操作

二、for 循环执行流程

来看:

css 复制代码
for i := 1; i <= 3; i++ {
	fmt.Println(i)
}

程序首先执行:

css 复制代码
i := 1

然后判断:

css 复制代码
i <= 3

条件成立,执行:

css 复制代码
fmt.Println(i)

之后执行:

css 复制代码
i++

此时:

ini 复制代码
i = 2

再次判断:

css 复制代码
i <= 3

直到:

ini 复制代码
i = 4

条件变成:

arduino 复制代码
false

循环结束。

整个流程可以理解为:

复制代码
初始化
↓
判断条件
↓
条件成立
↓
执行循环代码
↓
更新变量
↓
再次判断

三、循环变量从 0 开始

编程中非常常见的写法:

css 复制代码
for i := 0; i < 10; i++ {
	fmt.Println(i)
}

输出:

复制代码
0
1
2
3
4
5
6
7
8
9

为什么经常从 0 开始?

因为 Go 中:

复制代码
数组
切片
字符串

的索引都是从:

复制代码
0

开始。

例如:

go 复制代码
names := []string{"Tom", "Jack", "Lucy"}

索引:

css 复制代码
names[0] = Tom
names[1] = Jack
names[2] = Lucy

所以以后会大量看到:

css 复制代码
for i := 0; i < len(names); i++ {
}

四、使用 for 遍历切片

例如:

go 复制代码
package main

import "fmt"

func main() {
	names := []string{
		"Tom",
		"Jack",
		"Lucy",
	}

	for i := 0; i < len(names); i++ {
		fmt.Println(names[i])
	}
}

输出:

复制代码
Tom
Jack
Lucy

这里:

scss 复制代码
len(names)

获取切片长度。

然后:

css 复制代码
names[i]

根据索引取得元素。

五、倒序循环

for 不一定只能递增,也可以递减。

例如:

css 复制代码
for i := 10; i >= 1; i-- {
	fmt.Println(i)
}

输出:

复制代码
10
9
8
7
6
5
4
3
2
1

这种写法经常用于:

复制代码
倒计时
反向遍历
从后向前删除数据

六、一次增加多个数字

循环不一定每次:

css 复制代码
i++

也可以:

css 复制代码
i += 2

例如输出偶数:

css 复制代码
for i := 0; i <= 10; i += 2 {
	fmt.Println(i)
}

输出:

复制代码
0
2
4
6
8
10

例如:

css 复制代码
for i := 10; i <= 100; i += 10 {
	fmt.Println(i)
}

每次增加 10。

七、Go 的 while 风格循环

Go 没有:

arduino 复制代码
while

关键字。

但是可以使用:

复制代码
for 条件 {
}

实现相同效果。

例如:

css 复制代码
count := 1

for count <= 5 {
	fmt.Println(count)
	count++
}

输出:

复制代码
1
2
3
4
5

这基本等价于其他语言中的:

arduino 复制代码
while count <= 5

因此学习 Go 时可以记住:

arduino 复制代码
Go 的 for 同时承担 for 和 while 的作用

八、无限循环

如果 for 后面什么都不写:

复制代码
for {
}

就表示无限循环。

例如:

arduino 复制代码
for {
	fmt.Println("程序运行中")
}

程序会一直执行:

复制代码
程序运行中
程序运行中
程序运行中
......

这就是:

复制代码
死循环

但无限循环并不一定是错误。

例如服务器程序通常需要:

复制代码
启动
↓
等待请求
↓
处理请求
↓
继续等待

很多长期运行的程序内部都会存在类似无限循环。

九、使用 break 结束循环

无限循环通常需要一个退出条件。

例如:

markdown 复制代码
count := 1

for {
	fmt.Println(count)

	if count == 5 {
		break
	}

	count++
}

输出:

复制代码
1
2
3
4
5

当:

ini 复制代码
count == 5

执行:

kotlin 复制代码
break

循环立即结束。

break 下一篇会详细介绍,这里先理解它可以结束循环即可。

十、使用 continue 跳过本次循环

例如输出 1~10,但是不输出 5:

css 复制代码
for i := 1; i <= 10; i++ {
	if i == 5 {
		continue
	}

	fmt.Println(i)
}

输出:

复制代码
1
2
3
4
6
7
8
9
10

当:

ini 复制代码
i == 5

执行:

kotlin 复制代码
continue

本次循环剩余代码被跳过,然后继续下一次循环。

十一、for range

Go 提供了一种非常常用的遍历方式:

go 复制代码
for range

例如:

go 复制代码
names := []string{
	"Tom",
	"Jack",
	"Lucy",
}

for index, value := range names {
	fmt.Println(index, value)
}

输出:

复制代码
0 Tom
1 Jack
2 Lucy

这里:

diff 复制代码
index

表示索引。

复制代码
value

表示当前元素。

这种写法比:

css 复制代码
for i := 0; i < len(names); i++ {
	fmt.Println(i, names[i])
}

更加简洁。

十二、只需要值怎么办?

有时候我们不关心索引,只需要元素。

可以使用:

go 复制代码
for _, value := range names {
	fmt.Println(value)
}

这里:

复制代码
_

叫作空白标识符。

表示:

复制代码
这个返回值我不需要

输出:

复制代码
Tom
Jack
Lucy

这是 Go 中非常常见的写法。

十三、只需要索引

如果只需要索引,可以写:

go 复制代码
for index := range names {
	fmt.Println(index)
}

输出:

复制代码
0
1
2

没有必要写:

go 复制代码
for index, _ := range names {
}

通常直接:

go 复制代码
for index := range names {
}

更加简洁。

十四、range 遍历字符串

range 还可以遍历字符串。

例如:

arduino 复制代码
text := "Hello"

for index, char := range text {
	fmt.Println(index, char)
}

这里:

arduino 复制代码
char

得到的是:

go 复制代码
rune

如果希望直接输出字符:

arduino 复制代码
text := "Hello"

for index, char := range text {
	fmt.Printf("%d %c\n", index, char)
}

输出:

复制代码
0 H
1 e
2 l
3 l
4 o

十五、遍历中文字符串

range 在处理中文时非常重要。

例如:

arduino 复制代码
text := "学习Go"

for index, char := range text {
	fmt.Printf("%d %c\n", index, char)
}

输出类似:

复制代码
0 学
3 习
6 G
7 o

为什么索引不是:

复制代码
0
1
2
3

因为 Go 字符串使用 UTF-8 编码。

一个中文字符通常占多个字节。

range 会按照 Unicode 字符进行遍历,所以处理中文字符串时非常方便。

十六、for 循环求和

例如计算:

复制代码
1 + 2 + 3 + ... + 100

代码:

css 复制代码
sum := 0

for i := 1; i <= 100; i++ {
	sum += i
}

fmt.Println(sum)

输出:

yaml 复制代码
5050

这种:

bash 复制代码
sum += i

叫作累加。

统计:

复制代码
订单总金额
商品数量
访问次数
成绩总分

时经常使用。

十七、循环判断奇偶数

例如输出 1~20 中的偶数:

css 复制代码
for i := 1; i <= 20; i++ {
	if i%2 == 0 {
		fmt.Println(i)
	}
}

输出:

复制代码
2
4
6
8
10
12
14
16
18
20

这里综合使用:

markdown 复制代码
for
if
%
==

随着学习深入,我们会越来越多地组合前面学习过的语法。

十八、嵌套循环

for 中还可以继续写 for。

例如:

css 复制代码
for i := 1; i <= 3; i++ {
	for j := 1; j <= 3; j++ {
		fmt.Println(i, j)
	}
}

输出:

复制代码
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3

这种结构叫:

复制代码
嵌套循环

常见于:

复制代码
二维数据
表格
矩阵
组合计算

十九、九九乘法表

学习循环时经典案例就是九九乘法表。

css 复制代码
package main

import "fmt"

func main() {
	for i := 1; i <= 9; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf(
				"%d×%d=%d\t",
				j,
				i,
				i*j,
			)
		}

		fmt.Println()
	}
}

输出:

ini 复制代码
1×1=1
1×2=2  2×2=4
1×3=3  2×3=6  3×3=9
......

这里使用了两层 for:

复制代码
外层循环控制行
内层循环控制每一行内容

这是理解嵌套循环非常好的练习。

二十、遍历数据并查找目标

例如:

go 复制代码
users := []string{
	"Tom",
	"Jack",
	"Lucy",
	"Bob",
}

for _, user := range users {
	if user == "Lucy" {
		fmt.Println("找到用户:", user)
		break
	}
}

输出:

复制代码
找到用户: Lucy

找到目标后,没有必要继续遍历,所以直接:

kotlin 复制代码
break

结束循环。

这种逻辑在实际开发中非常常见。

二十一、遍历订单计算总金额

例如:

go 复制代码
prices := []int{
	100,
	200,
	50,
	300,
}

total := 0

for _, price := range prices {
	total += price
}

fmt.Println("订单总金额:", total)

输出:

复制代码
订单总金额: 650

这就是最基础的批量数据处理。

以后从数据库查询:

yaml 复制代码
1000 条订单

也可能通过循环逐条进行:

复制代码
统计
转换
过滤
处理

二十二、for 中变量作用域

例如:

css 复制代码
for i := 0; i < 10; i++ {
	fmt.Println(i)
}

这里:

css 复制代码
i

是在 for 中定义的。

循环结束以后:

css 复制代码
fmt.Println(i)

会报错。

因为 i 的作用域只在当前 for 语句中。

如果希望循环结束后继续使用:

css 复制代码
i := 0

for ; i < 10; i++ {
	fmt.Println(i)
}

fmt.Println("最终:", i)

此时可以正常访问。

二十三、避免死循环

初学者很容易写出:

css 复制代码
i := 1

for i <= 10 {
	fmt.Println(i)
}

这里忘记:

css 复制代码
i++

所以:

css 复制代码
i 永远等于 1

条件:

css 复制代码
i <= 10

永远成立。

程序会不断输出:

复制代码
1
1
1
1
......

因此使用条件循环时,一定要确认:

循环中的某些操作最终能够让条件变成 false。

二十四、不要在循环中进行不必要的重复操作

例如:

css 复制代码
for i := 0; i < len(users); i++ {
	// 数据处理
}

这种通常没有问题。

但如果循环内部存在非常耗时的操作:

go 复制代码
for _, user := range users {
	// 查询数据库
	// 请求远程API
	// 打开大文件
}

假设有:

复制代码
10000 个用户

就可能执行:

复制代码
10000 次数据库查询

造成严重性能问题。

实际开发中应该考虑:

复制代码
批量查询
批量写入
缓存
并发处理

所以学习 for 不仅要知道怎么循环,还要逐渐建立:

循环次数 × 单次操作成本

这样的性能意识。

二十五、for 和 range 如何选择?

如果需要控制索引:

css 复制代码
for i := 0; i < len(users); i++ {
}

非常合适。

如果只是遍历所有元素:

go 复制代码
for _, user := range users {
}

通常更加简洁。

如果需要索引和值:

css 复制代码
for i, user := range users {
}

如果需要无限循环:

复制代码
for {
}

如果需要 while 风格:

rust 复制代码
for condition {
}

Go 使用一个 for 就覆盖了这些不同场景。

二十六、完整实战案例

我们写一个简单的成绩统计程序:

go 复制代码
package main

import "fmt"

func main() {
	scores := []int{
		85,
		92,
		76,
		55,
		88,
		100,
	}

	total := 0
	passCount := 0

	for _, score := range scores {
		total += score

		if score >= 60 {
			passCount++
		}
	}

	average := float64(total) / float64(len(scores))

	fmt.Println("学生人数:", len(scores))
	fmt.Println("总分:", total)
	fmt.Printf("平均分:%.2f\n", average)
	fmt.Println("及格人数:", passCount)
}

输出:

复制代码
学生人数: 6
总分: 496
平均分:82.67
及格人数: 5

这里已经综合使用:

diff 复制代码
切片
for range
if
+=
++
类型转换
fmt.Printf

这也是学习编程非常重要的一点:

后面的知识并不是独立存在,而是不断组合前面学习过的语法解决实际问题。

二十七、常见错误

第一种是循环条件永远成立:

css 复制代码
i := 0

for i < 10 {
	fmt.Println(i)
}

忘记修改 i,会形成死循环。

第二种是数组越界:

css 复制代码
users := []string{"Tom", "Jack"}

for i := 0; i <= len(users); i++ {
	fmt.Println(users[i])
}

这里错误在:

scss 复制代码
i <= len(users)

应该:

scss 复制代码
i < len(users)

因为长度为 2 时,有效索引只有:

复制代码
0
1

第三种是不需要索引却声明索引:

go 复制代码
for index, value := range users {
	fmt.Println(value)
}

如果 index 没有使用,Go 会报:

复制代码
declared and not used

应该:

go 复制代码
for _, value := range users {
	fmt.Println(value)
}

二十八、本章小结

本篇详细介绍了 Go 中的 for 循环。

Go 虽然只有一个循环关键字,但使用方式非常灵活。

传统循环:

css 复制代码
for i := 0; i < 10; i++ {
}

while 风格:

rust 复制代码
for condition {
}

无限循环:

复制代码
for {
}

遍历数据:

go 复制代码
for index, value := range data {
}

只获取值:

go 复制代码
for _, value := range data {
}

只获取索引:

go 复制代码
for index := range data {
}

需要重点掌握:

  1. for 的初始化、条件和更新过程;
  2. 使用 i++i-- 控制循环;
  3. 使用 range 遍历数据;
  4. 使用 _ 忽略不需要的值;
  5. 使用 for 遍历字符串;
  6. 使用嵌套 for 处理多层数据;
  7. 使用 break 提前结束循环;
  8. 使用 continue 跳过当前循环;
  9. 注意数组、切片索引越界;
  10. 注意避免意外死循环。

掌握 for 后,我们已经基本学习完 Go 最核心的流程控制结构:

arduino 复制代码
if
switch
for

不过在循环和复杂流程控制中,还有三个非常重要的关键字需要单独学习:

kotlin 复制代码
break
continue
goto

本章练习

  1. 使用 for 输出 1~100。
  2. 使用 for 输出 1~100 中所有偶数。
  3. 计算 1~100 的总和。
  4. 使用倒序循环输出 10~1。
  5. 使用 for range 遍历一个字符串切片。
  6. 使用 range 遍历 "学习Go编程"
  7. 编写九九乘法表。
  8. 遍历成绩列表,统计及格人数。
  9. 遍历商品价格并计算总金额。
  10. 编写一个无限循环,并在满足条件时使用 break 退出。

下一篇预告

《Go break、continue、goto 详解》

下一篇将继续学习 Go 流程控制中的三个关键字:

kotlin 复制代码
break
continue
goto

主要包括:

  • break 结束循环;

  • break 结束 switch;

  • continue 跳过当前循环;

  • break 与 continue 的区别;

  • 多层循环中的 break;

  • 标签 Label;

  • 带标签的 break;

  • 带标签的 continue;

  • goto 基本语法;

  • goto 跳转规则;

  • goto 的实际使用场景;

  • 为什么业务代码中应该谨慎使用 goto。

掌握这些流程控制关键字后,我们就可以更加灵活地控制 Go 程序的执行过程。

相关推荐
程序员爱钓鱼40 分钟前
Rust impl详解:为Struct定义方法与关联函数
前端·后端·rust
weixin_431600441 小时前
NestJS 入门(9):连上数据库,SQL 写在哪?
数据库·后端·sql·学习·nest.js
qq_22589174661 小时前
基于Flask的城市地铁客流量数据预测系统设计与实现
后端·python·flask
IT_陈寒1 小时前
搞不定JavaScript的数组去重?你可能漏了这两个坑
前端·人工智能·后端
城管不管1 小时前
MySQL 慢查询完整排查
java·服务器·jvm·数据库·mysql·spring·面试
AINative软件工程2 小时前
AI Agent 证据仲裁工程实践:别让检索结果按自信程度撒谎
人工智能·后端·ai编程
卷无止境2 小时前
FastAPI 权限管理实战:从 ACL 到 RBAC 的那些门道
后端·python·fastapi
GreenTea10 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
风流 少年10 小时前
Spring AI 2.0:Memory
java·后端·spring