Golang Go语言中如何避免 Slice 的坑
Golang Go语言中如何避免 Slice 的坑
比如 slice 的 append,按我的理解:
1,扩容就生成新的底层数组,修改新的 slice 不会影响原来底层数组;
2,不扩容就不会生成新的数组,修改新的 slice 会修改原来的底层数组;
觉得这种问题很容易掉坑,求教有什么好习惯可以避免掉坑。
初始化的时候能知道大小最好,不然的话指定一个比较大的 capacity
这个结果是唯一的吧,会有第二种情况吗
如果 slice 作为参数接收到的咋办呢
其实《 The Go Programming Language 》 说的挺清楚的 上面也说尽量用数组的抽象 slice 很少直接用数组 加操作直接 append 减操作就[x:y] 截取
多踩几次坑就好了
通常情况下接收方都是作为消费者,只需读取数据,不需修改数据
如果函数不返回数组切片,就不要修改其内容
如果调用函数是为了获得一个数组的返回,那让函数自行 make,然后返回数组:
func getSlice() []int {
s := make([]int, …) // 在这个函数内部创建
///
return s
}
outS := getSlice()
如果函数既需要读取数组里的内容,又需要修改这个数组里的数据或追加数据,让函数接收一个数组同时返回一个数组
func modifySlice(s []int) []int {
s = append(s, …)
return s
}
outS = modifySlice(outS)
截取也是一个坑,截取后整个底层数组都释放不了了
这三个方法不错,
第三种情况有些疑问,如何保证 Outs 修改不会影响 s,或者 Outs 修改一定会影响 s,
还是说直接弃用 s,后面的操作都对 Outs 进行
slice 本身是一个结构体,里面有三个字段:当前 slice 第一个元素在底层数组的指针、slice 长度和 slice 容量。创建 slice 就是基于底层数组生成一个这样的结构体,append 的扩容就是新申请原底层数组一倍的内存,再把原底层数组的内容拷贝过来。大部分时候你不需要关心是否扩容,只要记住 slice 是指类型,作为参数传递会拷贝就行了golang<br>type slice struct {<br> array unsafe.Pointer<br> len int<br> cap int<br>}<br>
推荐阅读: https://research.swtch.com/godata
最近用 gopacket 这个包,里面有个类似的 Data()方法实现
https://gist.github.com/goofool/4f9e3514a65a97ffa9f0a5eb273fa1fb
结果:
https://gist.github.com/goofool/1bfa9898dc6495c64d7120f970278db9
#1 这个坑好像 JS 的
就当 cap 多些咯-_-|| 免得 append 又扩容
append 的时候又把底层数组覆盖了,哈哈😄
好巧,几十分钟前也刚看到这儿,看 reader 的 read 方法时想到的这个问题,马一下等 v 友的解答
对切片进行切片操作产生的是新的切片,不会影响原来的切片
如果这么容易引起无意操作,没有人提 issue 要求官方给个解决方案么
这个问题是因为 golang 参数都是值传递吧
原来是这样
一个 slice 就是一个 slice,不要把它理解成多个 slice+同一个 array
需要多个 slice,就用 copy
你就不用理会什么扩容不扩容的事情了,也不会觉得有什么坑了
是他们自己没学好而已
在Go语言中,Slice是一个非常强大且灵活的数据结构,但如果不小心使用,也容易遇到一些“坑”。以下是一些避免Slice常见问题的建议:
-
理解Slice底层结构:Slice是一个包含指向数组指针、长度和容量的结构体。当你对Slice进行切片操作时,如
s[1:3]
,会创建一个新的Slice,但它仍然引用原数组的内存。因此,修改新Slice的元素会影响原Slice(如果它们共享同一个底层数组)。 -
避免Slice越界:访问Slice元素时,确保索引在Slice的有效范围内(0到len(s)-1)。越界访问会导致运行时错误。
-
小心append操作:
append
函数可能会改变Slice的底层数组,如果Slice的容量不足以容纳新元素,会分配一个新的数组并复制原有元素。这可能导致原Slice的引用失效。如果函数返回Slice,并且你希望在函数外部看到append的结果,应该返回这个新Slice。 -
复制Slice:有时你可能希望完全独立于原Slice创建一个新的Slice副本。可以使用
copy
函数或append
函数(如append([]T(nil), s...)
)来实现。 -
Slice扩容策略:了解Slice的扩容策略可以帮助优化内存使用。Go语言在需要时会以倍增的方式扩容,但可以通过预先分配足够的容量来避免不必要的内存分配。
总之,避免Slice的坑关键在于理解其底层机制,并在使用时注意索引范围、append行为以及是否需要创建独立的Slice副本。