go 的 for 循环仅有一种语法但支持三类写法:标准三段式、while 风格(省略 init/post)、无限循环(全省略);for+defer 中 defer 在函数返回时执行而非每次循环结束。

Go 的 for 循环没有 while 或 do-while 形式,只有一种语法结构,但能覆盖全部场景——关键在于理解它的三段式本质和省略规则。
for 的三种写法及其适用场景
Go 的 for 表达式可省略任意部分,形成不同行为:
-
for init; condition; post:最常见,类似 C/Java,适合已知迭代次数或需步进控制(如遍历数组索引) -
for condition:省略 init 和 post,等价于 while;注意必须在循环体内修改条件变量,否则易成死循环 -
for:无任何子句,就是无限循环;退出靠break或return,常用于网络连接重试、事件监听等阻塞等待场景
示例:for i := 0; i 安全,但若 <code>s 是切片且长度不变,可提前计算 n := len(s) 避免每次迭代都调用 len() —— 虽然编译器常会优化,但显式写出更清晰。
range 遍历切片/映射时的陷阱
range 是 Go 中最常用的迭代方式,但它返回的是「副本」而非引用:
立即学习“go语言免费学习笔记(深入)”;
- 遍历切片时,
value是元素副本;直接修改value不会影响原切片 —— 想改必须用索引:s[i] = newValue - 遍历映射时,
key和value都是副本;value修改无效,且多次range映射顺序不保证,不能依赖遍历序 - 闭包中误用
range变量:所有 goroutine 共享同一个i或v变量,导致输出全是最后一个值;解决方法是传参或声明新变量:go func(i int) { ... }(i)
性能敏感场景下的 for 优化要点
高频循环中,微小差异会放大:
- 避免在
for条件中重复计算(如for i := 0; i ),提前赋值 - 切片遍历时,用
for i := range s比for i := 0; i 更简洁,且编译器对其做了专门优化(尤其对常量长度) - 需要同时访问索引和值时,优先用
for i, v := range s;手动维护索引反而容易出错,且无性能优势 - 不要为“省一次加法”而写
for i := len(s) - 1; i >= 0; i--倒序遍历——现代 CPU 分支预测对此类模式很友好,可读性更重要
for + defer 组合的常见误用
defer 在函数返回时才执行,不是每次循环结束就触发:
for i := 0; i 会打印 <code>2 2 2(因i是循环变量,最终值为 3,但 defer 捕获的是地址)- 正确做法是立即求值:
defer func(i int) { fmt.Println(i) }(i),或在循环内定义新变量:ii := i; defer fmt.Println(ii) - 更关键的是语义问题:多数人想在每次迭代后清理资源(如关闭文件),这时应直接调用
Close(),而不是 defer —— defer 是为函数级清理设计的
真正要注意的,是别把 range 的隐式副本、defer 的延迟时机、以及循环变量的复用这三者混在一起用——它们叠加时出的问题,往往调试半天才定位到根源。










