🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 延迟函数可能操作主函数的具名返回值 定义defer的函数,即主函数可能有返回值,返回值有没有名字没有关系,defer所作用的函数,即延迟函数可能会影响到返回值 ### 1 函数返回过程 有一个事实必须要了解,关键字*return*不是一个原子操作,实际上*return*只代理汇编指令*ret*,即将跳转程序执行。比如语句`return i`,实际上分两步进行,即将i值存入栈中作为返回值,然后执行跳转,而defer的执行时机正是跳转前,所以说defer执行时还是有机会操作返回值的。 举个实际的例子进行说明这个过程: ~~~go func deferFuncReturn() (result int) { i := 1 defer func() { result++ }() return i } ~~~ 该函数的return语句可以拆分成下面两行: ~~~go result = i return ~~~ 而延迟函数的执行正是在return之前,即加入defer后的执行过程如下: ~~~go result = i result++ return ~~~ 所以上面函数实际返回i++值。 ### 2 主函数拥有匿名返回值,返回字面值 一个主函数拥有一个匿名的返回值,返回时使用字面值,比如返回”1”、”2”、”Hello”这样的值,这种情况下defer语句是无法操作返回值的。 一个返回字面值的函数,如下所示: ~~~go func foo() int { var i int defer func() { i++ }() return 1 } ~~~ 上面的return语句,直接把1写入栈中作为返回值,延迟函数无法操作该返回值,所以就无法影响返回值 ### 3 主函数拥有匿名返回值,返回变量 一个主函数拥有一个匿名的返回值,返回使用本地或全局变量,这种情况下defer语句可以引用到返回值,但不会改变返回值。 一个返回本地变量的函数,如下所示: ~~~go func foo() int { var i int defer func() { i++ }() return i } ~~~ 上面的函数,返回一个局部变量,同时defer函数也会操作这个局部变量。对于匿名返回值来说,可以假定仍然有一个变量存储返回值,假定返回值变量为”anony”,上面的返回语句可以拆分成以下过程: ~~~go anony = i i++ return ~~~ 由于i是整型,会将值拷贝给anony,所以defer语句中修改i值,对函数返回值不造成影响 ### 4 主函数拥有具名返回值 主函声明语句中带名字的返回值,会被初始化成一个局部变量,函数内部可以像使用局部变量一样使用该返回值。如果defer语句操作该返回值,可能会改变返回结果。 一个影响函返回值的例子: ~~~go func foo() (ret int) { defer func() { ret++ }() return 0 } ~~~ 上面的函数拆解出来,如下所示: ~~~go ret = 0 ret++ return ~~~ 函数真正返回前,在defer中对返回值做了+1操作,所以函数最终返回1