回答for...range 遍历数组与切片的区别等三个Go语言问题

举报
Regan Yue 发表于 2021/11/27 17:01:19 2021/11/27
【摘要】 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() {...

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]

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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。