zoukankan      html  css  js  c++  java
  • 从GO内存模型与调用协议理解defer closure的坑

    资料参考:

    问题点

    • 非命名返回值
    package main
    
    import "fmt"
    
    func Test() (int) {
    	ret := 123
    	defer func() {
    		fmt.Println("first defer:",ret)
    	}()
    	defer func() {
    		ret++
    		fmt.Println("Inner:", ret)
    	}()
    
    	return ret
    }
    
    func main() {
    	fmt.Println(Test())
    }
    
    打印结果是123
    
    • 命名返回值
    package main
    
    import "fmt"
    
    func Test() (ret int) {
    	ret = 123
    	defer func() {
    		fmt.Println("first defer:",ret)
    	}()
    	defer func() {
    		ret++
    		fmt.Println("Inner:", ret)
    	}()
    
    	return ret
    }
    
    func main() {
    	fmt.Println(Test())
    }
    
    打印结果是124
    

    前者123, 后者124? 怎么理解!

    关键点

    Go的return语句不是原子指令! 底层被分解为:

    1. 返回值=xxx
    2. 调用defer函数
    3. 空的return
    

    其实这种理解也是错的!

    根据Go的内存模型及调用协议:

    • GO在调用栈(Stack)的方法帧(frame)里, 在参数(arguments)之上保留返回值(returnValue)的位置.
    方法帧(frame):
        返回值0
        返回值1
        ...
        实参0
        实参1
        ...
        
    
    • return语句设置返回值. 如果是命名返回值, 则可在defer closure中引用.
    • 执行defer函数栈.

    从这个层面理解, return语句也是原子指令.

    通俗说

    • 根据Go的内存模型与调用协议知道, 函数帧会保留前N个位置用于保存返回值. 如果返回值是命名的, 则这几个位置可以直接访问.
    • return语句将值设置到返回值的保留位置. 原则上说return语句也是原子指令.
    • defer栈return语句或函数结束后调用. 如果操作了返回值(命名返回值), 其值是可见的!
      需要明确的是, 非命名返回值一般不受影响.

    但也可以看出, 命名返回值某种程度节省了赋值指令.

  • 相关阅读:
    代码面试最常用的10大算法
    ant google compiler 压缩
    美工资源
    面试题
    validate表单验证插件
    laypage分页
    layer弹出框小结
    Thymeleaf
    webApp开发
    grunt自动化构建工具
  • 原文地址:https://www.cnblogs.com/zolo/p/7559989.html
Copyright © 2011-2022 走看看