zoukankan      html  css  js  c++  java
  • 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
    ", 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值发生变化


  • 相关阅读:
    iptables防火墙操作
    磁盘配置
    软件包安装
    网络配置
    用户、组及权限控制
    常见的Linux操作
    Vmware下CentOS 7安装步骤
    Spark(十四)【SparkSQL集成Hive】
    Scala和Java的List集合互相转换
    Spark(十)【RDD的读取和保存】
  • 原文地址:https://www.cnblogs.com/9527s/p/13353899.html
Copyright © 2011-2022 走看看