go基础第一篇:defer、select、range
defer
defer用于延迟函数的调用,每次defer都会把一个函数压入栈中,函数返回前再把延迟函数取出并执行。
题目1:
func main() { test1() } func test1() { var aInt = 1 defer fmt.Println(aInt) aInt = 2 return }
会打印1。
延迟函数的参数在defer语句出现时就已经确定了,后面不管如何修改aInt变量都不会影响延迟函数。
题目2:
func main() {
test2()
}
func test2() {
var aInt = 1
defer func(i int) {
fmt.Println(i)
}(aInt)
aInt = 2
return
}
会打印1。
延迟函数的参数在defer语句出现时就已经确定了。此处的延迟函数是个匿名函数,该匿名函数的入参在defer出现时就赋值了。
题目3:
func main() { test3() } func test3() { var aInt = 1 defer func() { fmt.Println(aInt) }() aInt = 2 return }
会打印2。
延迟函数的参数在defer语句出现时就已经确定了。此处的延迟函数是个闭包(在匿名函数里用到了外部变量,就是所谓的闭包),闭包只有在真正执行时,才会去读外部变量的值。
题目4:
func printArray(array [3]int) { for i := range array { fmt.Println(array[i]) } } func printArrayPointer(array *[3]int) { for i := range array { fmt.Println(array[i]) } } func test4() { var aArray = [3]int{1, 2, 3} defer printArray(aArray) defer printArrayPointer(&aArray) aArray[0] = 10 } func main() { test4() }
会打印10、2、3、1、2、3。
延迟函数的参数在defer语句出现时就已经确定了。如果是值类型(如数组),会发生拷贝,因此后续修改不会影响 defer 执行时的结果;如果是指针类型,虽然指针本身在defer时确定,但它指向的数据可能被修改,因此defer执行时看到的是最新数据。
题目5:
func test5() (result int) { i := 1 defer func() { result++ }() return i } func main() { a := test5() fmt.Println(a) }
会打印2。
return后面跟变量时,return语句不是原子的,如果函数是具名返回值函数,则会先把return关键字后面的变量赋值给具名返回值变量,然后再执行defer函数,最终返回该具名返回值变量的值。
即test5函数的实际执行过程是
i := 1
result := i
result++
最后返回result的值。
题目6:
func main() { a := test6() fmt.Println(a) } func test6() int { var i int defer func() { i++ }() return i }
会打印0。
return后面跟变量时,return语句不是原子的,如果函数是匿名返回值函数,则会先把return关键字后面的变量赋值给一个临时变量,然后再执行defer函数,最终返回该临时变量的值。
即foo函数的实际执行过程是
tmp := i
i++
最终返回tmp变量的值。
select
select块中,除default外,每个case都操作一个channel,要么读,要么写。如果所有case都阻塞,那么会执行default语句,如果没有default语句,那么select块会阻塞。空select块会阻塞。
浙公网安备 33010602011771号