两段代码
第一段:
```
func main() {
a, b := testDemo()
fmt.Println(a, b)
}
func testDemo() (int, int) {
var a int
return a, change(&a)
}
func change(a *int) int {
*a++
return 2
}
```
打印的结果是`1 2`
第二段代码
```
func main() {
a, b := testDemo()
fmt.Println(a, b)
}
func testDemo() (interface{}, int) {
var a int
return a, change(&a)
}
func change(a *int) int {
*a++
return 2
}
```
打印的结果是`0 2`
唯一的不同是testDemo这个函数的返回值是int还是interface{}
请教下为什么会有这样的不同呢?
####你这个问题其实包含两个问题:
1.其实你的第二段代码并非你所写的情况,编译器识别的testDemo函数应该是
···func testDemo() (interface{}, int) {
var a int
return interface{}(a), change(&a)
}···
只不过你写代码时省略了 interface{}(a)这个转化过程,但是编译器会帮你补上(暂时这么理解,其实是运行时执行的),这样它按照从左到右的顺序执行返回值,会先执行 interface{}(a),再执行change函数,然后赋值给return。
而你的第一段代码中并不需要执行 interface{}(a),所以会先执行change函数,然后赋值给return。
2.你的第一个返回值都不是传指针类型,所以不存在后执行的change函数给已存在的变量a重新赋值的问题。
具体证明你可以打开debug模式,单行调试,涉及interface{}参数或返回值(你的程序中两处:打印和testDemo)的地方,你会发现调用了runtime包下iface.go里的转化代码,涉及本代码二使用的是runtime.convT2E64(注释:`// The convXXX functions are guaranteed by the compiler to succeed.`说明此函数由编译器保证其正常执行)此时你再看作为值得elem参数,其存储之地址和调用前a的地址已经不一致,说明已经发生过数据拷贝,再结合266-267行```x = mallocgc(8, t, false)
*(*uint64)(x) = *(*uint64)(elem)```又申请一块新内存(uint64),可知虽然你的change返回地址,却因为编译器的原因已不可能越过以上代码返回到testDemo外层。
#1