Go中for循环的迭代变量必须在for语句内声明,不可提前在外层声明后复用,如禁止var i int; for i = 0; i < n; i++形式。

Go里for循环的迭代变量只能在for语句中声明
Go不允许在for外提前声明迭代变量再用于循环控制——比如不能像var i int; for i = 0; i 这样写(语法错误)。所有经典C风格的<code>for init; cond; post形式,init部分必须是短变量声明:=或var语句,且仅作用于该for作用域。
三种常见for写法及变量作用域差异
实际编码中容易混淆的是变量是否“复用”或“逃逸”。关键看声明位置和方式:
for i := 0; i :每次迭代<code>i都是同一个变量(地址不变),值被原地更新;闭包捕获时会出问题-
for _, v := range slice:每次迭代的v是新变量(Go 1.21+默认行为),但老版本中v是复用的,需v := v显式复制才能安全闭包 -
for i := range slice:i始终复用,行为稳定,无版本差异
闭包里用迭代变量必须显式拷贝
这是最常踩的坑。下面代码会输出5个5,而不是0 1 2 3 4:
<pre class="brush:php;toolbar:false;">for i := 0; i < 5; i++ {
go func() {
fmt.Println(i) // 全部打印5
}()
}
修复方法只有两种:
立即学习“go语言免费学习笔记(深入)”;
- 把
i作为参数传入匿名函数:<code>func(i int) { fmt.Println(i) }(i) - 在循环体内重新声明:
ii := i; go func() { fmt.Println(ii) }()
range遍历时v到底是不是新变量?看Go版本和编译器优化
Go 1.21起,for _, v := range s中的v默认每次迭代都分配新变量(除非确定不会逃逸)。但如果你用的是1.20或更早版本,v始终复用——这意味着:
- 直接取
&v地址,所有迭代拿到的是同一块内存 - 存入切片或传给goroutine前,必须
v := v强制拷贝 - 用
go vet能检测到这类潜在错误,建议开启
最稳妥的做法,是只要涉及地址、闭包、并发,就别依赖“v是否新建”,统一加一次赋值。


















