defer捕获的是变量地址而非值,循环中所有defer共享同一内存地址,执行时读取的是终值;正确做法是用i := i创建局部副本或显式传参。

defer 中变量捕获的常见误解
Go 的 defer 不是“记录变量值”,而是“记录变量的地址或引用”。如果在循环或条件分支中声明变量再 defer,容易误以为每次 defer 都捕获了当时的值,实际可能全指向同一个变量内存位置。
为什么 for 循环里直接 defer i 会打印多个相同数字
因为 i 是循环变量,整个循环共用一个 i 实例。所有 defer 语句都闭包捕获的是这个变量的地址,等真正执行时 i 已经变成终值(比如 3)。
常见错误写法:
for i := 0; i <p>解决方式只有两种:</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2525" title="Go语言(Golang)1.26.0"><img src="https://img.php.cn/upload/manual/001/589/237/6a6adeed24a4a355.png" alt="Go语言(Golang)1.26.0" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/gongju/2525" title="Go语言(Golang)1.26.0" class="overflowclass">Go语言(Golang)1.26.0</a> <p class="overflowclass">Go语言(Golang)1.26.0版本官方下载,版本号 1.26.0,适合旧项目维护、兼容性测试和指定版本开发环境搭建。</p> </div> <a rel="nofollow" href="/xiazai/gongju/2525" title="Go语言(Golang)1.26.0" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
- 用局部变量显式拷贝:在循环体内用
:=声明新变量,让每次 defer 捕获独立副本 - 把 defer 放进立即执行的匿名函数中,通过参数传值(本质也是做一次值拷贝)
推荐写法:
defer
- 基础类型(
int,string):参数求值即拷贝值,安全 - 指针(
*T):求值结果是地址,defer 执行时读的是该地址当前内容 - 切片(
[]T):求值结果是底层数组指针+长度+容量,若原切片后续被append导致扩容,defer 里看到的仍是旧底层数组(可能已被覆盖) - 结构体:按字段逐个求值;若含指针字段,该字段仍指向运行时最新地址
所以不要假设 defer 能“冻结”复杂数据状态——它只冻结参数表达式的求值结果,不冻结运行时内存内容。
最易被忽略的是:哪怕用了 i := i 这种技巧,如果 defer 的是 func() { ... } 且里面访问的是外部变量(非参数),依然会读到最终值。捕获逻辑只作用于 defer 语句那一刻的参数和变量绑定,不递归冻结闭包环境。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










