你以为面试中的 defer 是在考 defer 吗?并不是,其实是在考 函数变量的作用域
以下这是 go语言爱好者 97 期的一道题目。要求很简单, 代码执行 i, j 的值分别是什么。
func Test_Demo(t *testing.T) {
i := 10
j := hello(&i)
fmt.Println(i, j)
}
func hello(i *int) int {
defer func() {
*i = 19
}()
return *i
}
这道题虽然代码少, 但是考点还是蛮多的
核心: 函数变量作用域
defer 执行时间
闭包
指针
知识点
这里面所有的内容都可以在 Effective Go 中解决
贪婪算法
什么是贪婪算法, 就是找到局部最优解, 合并后就是全局最优解。
怎么找局部最优解, 就是要 对事情进行抽象,掌握事情的本质 。
defer 延迟执行
FILOreturn
openclose
defer
因此 defer 有什么好考的, 而且实际场景代码也不会那样写(违反了可读性的这一基本之准则)。
所以通常面试中有 defer 的问题都不是在考 defer , 只不过是披上了 defer 的狼皮。
函数及返回值
其实 go 中关于函数返回花样还是挺多的。
func NamedResult(i, j int) (x int)return
感觉和 golang 本身的代码可读性的的理念有一点冲突。就像为什么不支持三元运算符一样。其实这样本身也没有什么, 就是一两个 死记硬背 的知识点而已。
defer闭包指针
如果对 函数变量的作用域 理解不清楚的话, 就容易掉坑。
package main
// 命名结果
func NamedResult(i, j int) (x int) {
x = i + j
// 默认返回
return
}
// 匿名结果
func UnnamedResult(i, j int) int {
// 指定返回
return i + j
}
我们开启汇编, 查看一下函数过程
go tool compile -N -l -S main.go
从汇编结果可以看到:
UnnamedResult~r2NamedResultxRET(SP)MOVQ AX, "".~r2+24(SP)
既然如此, 我们就将所有函数的写法全部统一, 不再区分 命名的、 匿名的 , 默认的, 指定的
命名返回值
return 指定结果
func ReformResult(i, j int) (r2 int) {
r2 = i + j
return r2
}
这样看起来, 整个函数就清晰的多了。
实战练习一下
根据之前所说, 我们这里来对函数做一下整形手术。
func Test_reformDemo(t *testing.T) {
i := 10
j := reformHello(&i)
fmt.Println(i, j) // 19 10
}
// hello 原函数
func hello(i *int) int {
defer func() {
*i = 19
}()
return *i
}
// reformHello 整形函数
// reform 1. 匿名变命名
func reformHello(i *int) (_x int) {
// reform 2. return 拆分
// reform 2.1 显式赋值
_x = *i // _x=10
// reform 3. defer 在返回前执行
func() { *i = 19 }() // *i=19
// reform 2.2 显式返回
return _x // _x=10
}