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)