golang闭包和range,关于闭包内使用外部变量
遇到经典问题
func mian() {
resslice := []int{1, 2, 3, 4}
for _, v := range resslice {
fmt.Println(v)
defer fun1(v)
}
}
func fun1(value int) {
fmt.Println(value)
}
输出结果为
1
2
3
4
4
3
2
1
正常传递参数,值传递,运行到defer时把当时的v值传递给函数,最后结束时按defer规律执行
resslice := []int{1, 2, 3, 4}
for _, v := range resslice {
fmt.Println(v)
defer func() {
fmt.Println(v)
}()
}
改为闭包输出结果为
1
2
3
4
4
4
4
4
for range的内部大概是这样
for_temp := v
len_temp := len(for_temp)
for index_temp = 0; index_temp < len_temp; index_temp++ {
value_temp = for_temp[index_temp]
index = index_temp
value = value_temp
v = append(v, index)
}
循环前把值复制给 for_temp 然后用同一个变量进行赋值
因为闭包里的非传递参数外部变量值是传引用的,是闭包是地址引用
闭包的v 引用外部变量v,把外部的v地址拷贝了一份,执行到最后v的值是4,所以最后输出为连续的4
增加例子方便理解
package main
import "fmt"
func main() {
x, y := 1, 2
defer func(a int) {
fmt.Printf("x:%d,y:%d\n", a, y) // y 为闭包引用
}(x) // 复制 x 的值
x += 100
y += 100
fmt.Println(x, y)
}
输出结果
101 102
x:1,y:102
闭包不加接收的参数也就是x,就会默认使用外部变量的地址拷贝,defer是最后执行的,
在执行前y值已经被改变,defer里使用y地址取值,结果是已经改变的值。
同样的如果不是defer,使用go,闭包函数并发执行,也可能主线程的值先被改变,go出去的线程用y地址取值,y值发生变化