Go:在defer指定的函数中修改返回值会出现的几种情况

作者: 李佶澳   转载请保留:原文地址   更新时间:2018-12-07 10:54:23 +0800

说明

Go语言的手册中Defer statements 明确说了可以在defer中修改命名的返回的变量(named result parameters )

For instance, if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned. If the deferred function has any return values, they are discarded when the function completes. (See also the section on handling panics.)

但是对修改非命名的返回变量,却没有明确的说法。

试验

// Create: 2018/12/07 10:03:00 Change: 2018/12/07 10:03:00
// FileName: a.go
// Copyright (C) 2018 lijiaocn <[email protected]>
//
// Distributed under terms of the GPL license.

package main

//change in defer,result is 2
func det1() int {
	result := 2
	defer func() {
		println("det1")
		result = 1
	}()
	return result
}

//change pointer in defer, result is 2
func det2() int {
	result := 2
	defer func(result *int) {
		println("det2")
		*result = 1
	}(&result)
	return result
}

//use name result and change in defer, result is 1
func det3() (result int) {
	result = 2
	defer func() {
		println("det3")
		result = 1
	}()
	return result
}

//return is pointer,result is 1
func det4() *int {
	result := 2
	defer func() {
		println("det4")
		result = 1
	}()
	return &result
}

func main() {
	a := det1()
	b := det2()
	c := det3()
	d := det4()
	println(a)
	println(b)
	println(c)
	println(*d)
}

输出结果如下:

det1
det2
det3
det4
2
2
1
1

其中最让人不能理解的是det2(),defer中修改的是指针指向的内容,但是返回的结果没有变化:

//change pointer in defer, result is 2
func det2() int {
	result := 2
	defer func(result *int) {
		println("det2")
		*result = 1
	}(&result)
	return result
}

出现这种情况,只有一个解释,在defer指定的函数执行之前,函数的返回值就已经确定了,defer中的更改是不生效的。

但是det4()中的修改又是有效的:

//return is pointer,result is 1
func det4() *int {
	result := 2
	defer func() {
		println("det4")
		result = 1
	}()
	return &result
}

结合det2()的结果推断,return的返回值不仅是在defer指定函数执行之前确定的,而且是拷贝了一份。

det3()又怎样解释呢?return的返回值是命名变量的时候,不做拷贝?

//use name result and change in defer, result is 1
func det3() (result int) {
	result = 2
	defer func() {
		println("det3")
		result = 1
	}()
	return result
}

分析一下汇编代码

用下面的命令编译:

GOARCH=amd64 GOOS=linux go build

然后将得到的二进制程序反汇编:

go tool objdump -S defer >defer.asm    //用go tool反汇编

objdump -d -t defer  >defer.asm        //linux上用objdump反汇编

有空继续分析..(2018-12-07 14:04:14)

参考

  1. X86 Assembly/GAS Syntax
  2. Intel® 64 and IA-32 Architectures Software Developer’s Manual:Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z
  3. CPU的相关知识
  4. Using as

本文原创首发于网站:www.lijiaocn.com

QQ交流群

区块链实践互助QQ群:576555864

Kubernetes实践互助QQ群:947371129

Prometheus实践互助QQ群:952461804

Kong/Envoy实践互助QQ群:952503851

Ansible实践互助QQ群:955105412

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作 友情链接: lijiaocn github.com