zoukankan      html  css  js  c++  java
  • golang关于一些新手不注意会出现的小问题

    前言

    最近在整理之前写程序,学习时所记录的有道云笔记,发现一些有意思的小点跟大家分享一下。如有错误请大家给指出

    一、闭包 defer

    闭包(匿名函数)

    func test(){
        i, n := 1 ,2;
        defer func(a int){
            fmt.Println("defer:", a , n); //n被闭包引用
        }(i) //复制i的值
        i , n = i+1,n+2;
        fmt.Println(i , n);
    }

    我们看一下结果:

    2 4
    defer: 1 4

    为什么会这样?是因为闭包复制的是原对象指针出现了延迟引用现象 (加上defer的延迟调用,正好可以解释上面程序的延迟引用现象)。我们在使用闭包的时候要注意这个问题,同样在for 循环中 也会出现类似现象。

    感谢“”小强”,增加一个闭包的例子,期望能给大家带来更多的理解

    func b(){
    	i := []int{1,2,3,4}
    	for _,n := range i {
    		go func(){
    			fmt.Println(n); // n 被闭包引用,引用n的内存地址
                                            // 协程G 实际上执行的是匿名函数对象  FuncVal { func_address, closure_var_pointer ... } 
    		}();
    		fmt.Println(n);
    	}
    	time.Sleep(1*time.Second);
    
    }
    

      

     程序有一定的不确定性 ,但是输出结果反映了内存引用现象

    1
    2
    3
    4
    4
    4
    4
    4

     二、Map

    前一段时间在论坛看到一个问题

    type Data struct{
        AABB [2]float64
    }
    
    var m map[string]Data = make(map[string]Data,1)
    
    m["xxx"] = Data{}
    
    m["xxx"].AABB[0]=1.0
    m["xxx"].AABB[1]=2.0
    #以上代码go build 通不过,错误提示cannot assign to m["xxx"].AABB[0]

    这是一个网友给出的答案

    type Data struct{
        AABB [2]float64
    }
    
    m := make(map[string]*Data,1)
    m["xxxx"] = &Data{}
    m["xxxx"].AABB[0] = 1.0 
    m["xxxx"].AABB[1] = 2.0 
    #这样写就对了,你的 m["xxxx"] 返回的是值,不是一个可取地址的变量

    这个网友的答案可以编译成功,但是不可取,他犯了很多新手都容易出现的问题

    why?Golang中的map元素属性被设计为只读的,并不期望被修改,并且从 map 中取回的是一个value也是临时复制品。并且map是一个hash 结构,当hash扩容时,键值的存储位置就会发生改变。如果这个时候我们对 m["xxxx"].AABB[0] = 1.0 修改,不知道指针会发什么。大家有兴趣可以看看Go Hashmap内存布局和实现

     如果我们想修改最好这样

    type Data struct{
        AABB [2]float64
    }
    
    m := make(map[string]*Data,1)
    m["xxxx"] = Data{}
    d := m["xxxx"]
    d.AABB[0] = 1.0 
    d.AABB[1] = 2.0 
    m["xxxx"] = d
    

    三、nil

    先看一段代码,当然这种场景不常见,但是能让我们更好的理解nil

    func t(){
    	var i *int = nil;
    	var n interface{}  = i;
    	fmt.Println(n==nil); //false 
    }

    可能很多小伙伴都会有疑问都是nil 为啥会不相等。我们先分别看一下pointer,interface的结构体和当pointer,interface为nil时的结构

    uintptr 
    type interfaceStruct struct {
      v *_value // 实际值
      t *_type // 实际值的类型信息 
    }
    uintptr(0) == nil
    type interfaceStruct struct {
      v:uintptr(0)
      t:uintptr(0)
    } == nil
    

      

    由此我们可以看出nil其实就是指针 interface的零值

    这时候我们在来解释为啥为flase就很容易了

    func t(){
    	var i *int = nil;    // (*int)nil
    	var n interface{}  = i; //  interace{}((*int)nil)
    	fmt.Println(n==nil); // type interfaceStruct struct {
                       //     v: uintptr(0),
                       //     t: (*int)
                       // }
    }
    

      

    官方文档规定可以为nil的类型还有 slice ,map ,channel ,function 。

    可能有些朋友可能会问为啥没有error类型,那是因为error 只是程序预设的接口方法, err nil 也会出现类似的问题,官方有一个文档也给出了解释,传送门

    type error interface { 
        Error() string 
    }  

    感谢阅读,欢迎提供建议

  • 相关阅读:
    vue 单页面应用 app自适应方案
    css3-3D特效
    css3动画-transition
    html5基本页面
    初入博客园
    网络部分之如何发送HTTP请求
    多线程知识之NSOperation的使用
    多线程知识点之GCD的使用
    多线程知识点之NSThread的使用
    plist 文件读写
  • 原文地址:https://www.cnblogs.com/turnswing/p/7999992.html
Copyright © 2011-2022 走看看