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值发生变化


 

posted @ 2020-07-21 11:00  海拉尔  阅读(639)  评论(0编辑  收藏  举报