写点什么

Go 语言学习查缺补漏 ing Day9

作者:Regan Yue
  • 2021 年 11 月 27 日
  • 本文字数:1452 字

    阅读完需:约 5 分钟

Go 语言学习查缺补漏 ing Day9

一、又谈 defer 的执行顺序

请看下面这段代码:


package main
import ( "fmt")
func f(n int) (r int) { defer func() { r += n recover() }() var f func() defer f() f = func() { r += 8 } return n + 2}func main() { fmt.Println(f(3))}
复制代码


程序运行结果是:


8
复制代码


为什么呢?执行 f(3)时,这段代码会先执行 return 中的 n+2,然后执行 defer 语句,因为 defer 语句是先进后出,故先执行 defer f(),然后因为此时 f()未定义,所以会导致 panic 异常读书,不过,别急,我们下一个 defer 语句使用 recover()将异常回收了,然后 r += n,也就是 r 加 3,因为 r+8 未执行,所以和是 3+2+3=8.

二、切片底层数组的一个有关问题

再看下面这段代码:


package main
import ( "fmt")
func change(s ...int) { s = append(s, 8)}func main() { slice := make([]int, 5, 5) slice[0] = 0 slice[1] = 1 fmt.Println(slice) change(slice...) fmt.Println(slice) change(slice[0:2]...) fmt.Println(slice) change(slice[0:3]...) fmt.Println(slice) change(slice[0:4]...) fmt.Println(slice) change(slice[0:5]...) fmt.Println(slice)}
复制代码


你认为输出结果是什么?


下面是输出结果。你答对了吗?


[0 1 0 0 0][0 1 0 0 0][0 1 8 0 0][0 1 8 8 0][0 1 8 8 8][0 1 8 8 8]
复制代码


答对的小伙伴可以看下一点啦~


答错的小伙伴来听我解释一下吧~


如果函数 change(s ...int)的参数类型使用...,可以将 slice 切片传入而不会新建切片。这里第一次调用 change()函数前,创建的切片容量和长度是相等的,所以调用 change()函数后,切片进行扩容,会导致生成新的切片,8 也加在新的切片上,原来的切片不会发生变化。


change(slice[0:2]...)fmt.Println(slice)
复制代码


这行代码使用[0:2]截取生成了一个新的切片,但是它的底层数组与之前的切片的底层数组是一样的,不过新切片的长度是 2,而之前切片的长度是 5.所以在这个切片上 append 元素我们打印旧切片 slice 能够看到,也就是:


[0 1 8 0 0]
复制代码


然后我们一直截取一直增加,直到:


change(slice[0:5]...)fmt.Println(slice)
复制代码


此时切片的容量和长度已经加到一致了,再 append 来添加元素就会导致扩容,而导致底层数组变化。所以再次打印 slice 切片,就会还是:


[0 1 8 8 8]
复制代码

三、for...range 遍历数组与切片的区别

package main
import ( "fmt")
func main() { var a = [5]int{1, 2, 3, 4, 5} var r [5]int for i, v := range a { if i == 0 { a[1] = 8 a[2] = 8 } r[i] = v } fmt.Println("r = ", r) fmt.Println("a = ", a)}
复制代码


先来看一看运行结果:


r =  [1 2 3 4 5]a =  [1 8 8 4 5]
复制代码


我们可以看到,遍历时保存的 r 数组内的值与最终输出的数组不一致,我们前面的文章说过,这是因为 for...range 遍历数组时,会生成一个副本,然后遍历这个副本,所以我们在循环中修改数组的值并不会修改副本的值,所以导致两个数组结果不一致。


而切片就不一样了,我们来看一看切片会有什么运行结果:


package main
import ( "fmt")
func main() { var a = []int{1, 2, 3, 4, 5} var r [5]int for i, v := range a { if i == 0 { a[1] = 8 a[2] = 8 } r[i] = v } fmt.Println("r = ", r) fmt.Println("a = ", a)}
复制代码


这是运行结果:


r =  [1 8 8 4 5]a =  [1 8 8 4 5]
复制代码


我们可以看到两个是相同的,这是因为虽然是副本,但是指向的是同一个底层数组,因此修改副本的底层数组也会修改原切片的底层数组。

发布于: 12 分钟前阅读数: 4
用户头像

Regan Yue

关注

还未添加个人签名 2020.08.12 加入

对Go、Python、网络安全、区块链感兴趣. · 华为云云享专家 · 掘金资讯创作者

评论

发布
暂无评论
Go语言学习查缺补漏ing Day9