我有一个零片:
var s1 []int // len(s1) == 0, cap(s1) == 0
Run Code Online (Sandbox Code Playgroud)
我附加一个元素:
s2 := append(s1, 1) // len(s2) == 1, cap(s2) == 2
Run Code Online (Sandbox Code Playgroud)
为什么将一个元素附加到零切片会使容量增加2?
使用打印切片fmt.Printf显示以下内容:
[] // s1
[1] // s2
Run Code Online (Sandbox Code Playgroud)
我也很困惑为什么重新切片s2[0:2]显示零,这不是在原始切片中,也不是附加到它:
[1,0] // s2[0:2]
Run Code Online (Sandbox Code Playgroud)
Rob*_*ier 17
Go可以免费为您提供超出您要求的容量.这通过减少所需的分配(以及可能的复制)数量来提高性能.容量只是在需要另一个分配之前保留的空间量.
如果你向这个切片添加5个元素,至少在我的实验中,容量是8.这不应该是令人惊讶的,但也不应该依赖.在不同平台或编译器的不同版本上,只要容量"足够大"(等于或大于长度),实际结果可能不同.
切片的上部索引边界定义为其容量:
对于数组或字符串,如果0 <= low <= high <= len(a),则索引在范围内,否则它们超出范围.对于切片,上限索引是切片容量上限(a)而不是长度.常量索引必须是非负的,并且可以通过int类型的值表示; 对于数组或常量字符串,常量索引也必须在范围内.如果两个指数都是常数,则它们必须满足低<=高.如果索引在运行时超出范围,则会发生运行时混乱.
这就是为什么阅读超过长度不会导致恐慌.即便如此,你也不应该把那些零视为切片的一部分.它们可以通过切片进行索引,但是fmt.Printf(s2)会正确地显示它们,因为它们不是切片的一部分.不要这样下标.
一般来说,你想看长度,而不是容量.容量主要是可读的,以帮助进行性能优化.
我认为,此处的容量和长度有些混乱。当您打印切片并看到切片中的零个或一个元素时,您正在查看的是其长度,即切片实际包含的值数。除非您使用cap()内置命令查找基础数组,否则通常不会向您隐藏基础数组的容量。
实际上,切片实际上是固定长度的数组。当切片中的空间用完时,Go必须通过创建一个新的(更长的)数组并从旧数组复制所有值来使其更大。如果您要向切片添加大量值,则每次为新值分配内存(并复制所有旧值)会非常慢,因此有时Go会假设您要追加更多元素,并且继续进行,分配的内存超出了需要,因此您不必经常复制内容。下次调用追加时可以使用此额外的内存,并且在必须扩展切片之前可以在切片中存储的值的数量称为其容量。。换句话说,条带的容量是条带支持阵列的长度,条带的长度与容量无关。
当您将单个值附加到切片中时,Go会发现它必须为此值分配空间,因此它将分配实际所需空间的两倍,将长度增加1,容量增加2。
您提到的切片是因为切片作用于基础数组:Go使您可以切片超过切片的长度,而不能超出切片的容量(底层数组的长度)。例如,让我们在一个简单的nil切片上尝试一些操作:
var s []int
fmt.Println(len(s), cap(s)) // Prints `0 0` because this is a nil slice
s = append(s, 1)
fmt.Println(len(s), cap(s)) // Prints `1 2`
fmt.Println(s[0:2]) // Prints `[1 0]` (only the first value is part of the slice, the second position in the underlying array is a zero value that is waiting to be used when the slices length grows)
fmt.Println(s[0:3]) // panic: slice bounds out of range (because we've exceeded the slices capacity)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8885 次 |
| 最近记录: |