Go语言defer行为差异面试题:两段代码输出不同的原因解析
Go中Defer在不同返回值声明下的行为差异解析
咱们先看两段看似相似但输出截然不同的Go代码:
第一段代码(输出1)
func test() int { var x int defer func() { x++ }() x = 1 return x } func main() { fmt.Println(test()) }
输出结果:1
第二段代码(输出2)
func test() (x int) { defer func() { x++ }() x = 1 return } func main() { fmt.Println(test()) }
输出结果:2
核心差异:返回值的声明方式
这两段代码的本质区别在于返回值是匿名还是命名,结合反汇编我们能更清晰看到底层逻辑:
第一段代码的反汇编逻辑分析
在第一段的test函数里,x是函数内部的普通局部变量。当执行return x时,Go会先把x的当前值(也就是1)拷贝到函数的返回值栈帧位置,之后才会执行defer函数。而defer里的x++修改的是函数内部的那个局部变量x,和已经拷贝到返回位置的值没有关联,所以最终返回的是之前拷贝好的1。
从反汇编指令也能验证这一点:return x对应的操作会完成值的拷贝,后续defer的操作只影响栈上的局部变量,不会触碰到已经准备好的返回值。
第二段代码的反汇编逻辑分析
第二段的test函数用了命名返回值((x int)这种声明方式)。这时候,x从一开始就不是普通局部变量——它就是函数返回值对应的栈内存变量。当执行return时,Go不需要做值拷贝,因为返回值变量本身就是x。之后执行defer函数里的x++,直接修改的就是这个返回值变量,所以最终返回的是修改后的2。
反汇编里能看到,这段代码的return指令没有额外的拷贝操作,defer的操作直接作用于返回值对应的内存位置,自然会改变最终的返回结果。
总结一下
- 匿名返回值:return时会把局部变量的值拷贝到返回栈帧,defer修改的是局部变量,不影响最终返回值
- 命名返回值:return时直接使用命名变量作为返回值,defer修改这个变量会直接改变返回结果
内容的提问来源于stack exchange,提问作者frankegoesdown
相关产品推荐
相关产品推荐

