共计 2110 个字符,预计需要花费 6 分钟才能阅读完成。
面试题
最近 Go 101 的作者公布了 11 道 Go 面试题,十分乏味,打算写一个系列对每道题做具体解析,欢送大家关注。
本题是 Go quiz slice
系列的第 2 道题目,这道题十分有迷惑性。
通过这道题咱们能够通晓对 slice
做 range 遍历的坑,防止在理论我的项目中踩坑。
package main
func main() {var x = []string{"A", "B", "C"}
for i, s := range x {print(i, s, ",")
x[i+1] = "M"
x = append(x, "Z")
x[i+1] = "Z"
}
}
- 0A,1B,2C,
- 0A,1Z,2Z,
- 0A,1M,2M,
- 0A,1M,2C,
- 0A,1Z,2M,
- 0A,1M,2Z,
- (infinite loop)
大家能够在评论区留下你们的答案。这道题次要有以下几个考点:
slice
做 range 遍历,Go 编译器背地会做哪些事件?slice
什么时候扩容,扩容后的行为是怎么样的?
解析
咱们先一一解答下面的问题。
range 遍历机制
range 对 slice
做遍历的时候,实际上是先结构一个原 slice 的拷贝,再对这个拷贝做遍历。
在 for 循环外面的逻辑执行之前,这个拷贝的值就确定下来了。因而这个拷贝的长度和容量是不会在 for 循环的时候产生扭转的。
以下面的题目为例:range x
实际上是会先结构一个原切片 x
的拷贝,咱们假如为 y
,而后对y
做遍历。
for i, s := range x {print(i, s, ",")
x[i+1] = "M"
x = append(x, "Z")
x[i+1] = "Z"
}
下面这段代码能够等价为:
y := x
for i := 0; i < len(y); i++ {print(i, y[i], ",")
x[i+1] = "M"
x = append(x, "Z")
x[i+1] = "Z"
}
slice 扩容机制
通过 append
函数给 slice
增加元素的时候,有 2 种状况:
- 如果切片的容量足够,就会在切片指向的底层数组里追加元素。
- 如果切片的容量不足以承载新增加的元素,就会开拓一个新的底层数组,把原切片里的元素拷贝过去,再追加新的元素。切片构造里的指针会指向新的底层数组。
答案
咱们回到本文最开始的题目,逐行解析每行代码的执行后果。
代码 | 程序执行后果 |
---|---|
var x = []string{“A”, “B”, “C”} | x 是一个切片,长度是 3,容量是 3,x 指向的底层数组的值是[“A” “B” “C”] |
for i, s := range x | 编译器先结构一个切片 x 的拷贝,假如为切片 y ,而后对y 做遍历。y 的值在 for 循环执行前就确定下来了,长度为 3,容量为 3,固定不变。 |
print(i, s, “,”) | 第一次 for 循环,i 的值是 0,s 的值是切片 y 里下标索引为 0 的元素,值为 ”A”,打印 0A |
x[i+1] = “M” | 执行 x[1] = “M”,因为切片 x 和y 当初指向同一个底层数组,切片 y 里下标索引为 1 的元素的值也被改成了 ”M”,y 指向的底层数组的值为[“A”, “M”, “C”] |
x = append(x, “Z”) | 给切片 x 增加新元素 ”Z”,因为以后切片 x 的长度为 3,容量为 3,容量已满,不足以承载新减少的元素,所以要对 x 的底层数组做扩容,x 指向新的底层数组,新底层数组的值是 [“A”, “M”, “C”, “Z”],y 还是指向原来的底层数组,y 指向的底层数组的值是[“A”, “M”, “C”] |
后续 for 循环 | 因为从第 2 次 for 循环开始,x 和 y 指向了不同的底层数组,所以对切片 x 的批改不会影响到y ,因而前面打印的后果顺次是 1M,2C |
所以本题的答案是 0A, 1M, 2C。
总结
对于 slice,时刻想着对 slice 做了批改后,slice 里的 3 个字段:指针,长度,容量是怎么变的。
zen of go
- 对切片
x
做 range 遍历,实际上是对x
的拷贝 (假如为y
) 做 range 遍历,y
的值 (包含y
构造体里指向底层数组的指针的值,y
的长度和容量)都在执行for
循环前确定下来了。 - 切片的底层数据结构和扩容机制,如果有不分明的,参考我写的 slice 底层原理篇,蕴含了 slice 的所有注意事项。
开源地址
文章和示例代码开源地址在 GitHub: https://github.com/jincheng9/…
公众号:coding 进阶
集体网站:https://jincheng9.github.io/
思考题
留下 2 道思考题,欢送大家在评论区留下你们的答案。也能够在我的 wx 公号发送音讯 slice2
获取答案和起因。
-
题目 1:
package main func main() {var x = []string{"A", "B", "C"} for i, s := range x {print(i, s, " ") x = append(x, "Z") x[i+1] = "Z" } }
-
题目 2
package main func main() {var y = []string{"A", "B", "C", "D"} var x = y[:3] for i, s := range x {print(i, s, ",") x = append(x, "Z") x[i+1] = "Z" } }
References
- https://go101.org/quizzes/sli…
- https://mp.weixin.qq.com/s?__…
- https://jincheng9.github.io/p…