问题的背景:
切片(slices)是Go中的一种关键动态结构,作为固定长度数组的替代方案出现,以提高便利性和内存利用率。它们提供对数组子集的灵活操作,但具有许多细微差别,对于高效和安全的代码至关重要。
问题:
许多开发人员不理解切片是如何构造的:切片(slice)并不是数组本身,而是一个包含指向数组的指针、长度和容量(capacity)的结构。这可能导致内存泄漏、复制时的错误以及在更改原始数组时出现意外效果。
解决方案:
切片是一种类型:
type slice struct { ptr unsafe.Pointer len int cap int }
通过append()扩展切片时,可能会发生后备数组的重新分配,而所有对旧数组的引用仍然有效,但将引用旧数据。不了解这一特性会导致错误和内存泄漏。
正确分配内存和复制的示例:
src := []int{1,2,3,4,5} dst := make([]int, len(src)) copy(dst, src)
使用 [:] 创建的切片共享基础数组,如果未执行复制,则它们的修改会相互影响。
主要特点:
如果通过append超过cap增加切片,而其他切片引用同一数组,会发生什么?
当超过cap时,append会创建具有新内存位置的基础数组,只有这个切片引用新数组,而其他切片仍然引用旧数组。这是造成数据不一致的常见原因。
为何重要的是不要保存从大数组中获取的小切片?
即使切片非常小,其指针仍然引用整个后备数组,这可能导致大数组在内存中被保留,并造成内存泄漏。
如果切片超出数组边界会发生什么?
将导致panic:runtime error: slice bounds out of range。
函数读取一个大文件到字节数组并返回前100个元素的切片。这个切片随后被长时间持有,但大数组的所有内存都留在GC中。
优点:
缺点:
在获取切片后,立即将所需部分复制到使用make和copy创建的新切片中。旧数组立即被遗忘,GC释放内存。
优点:
缺点: