假设如下代码:
func f() {
x, y := make([]byte, 9000), make([]byte, 9)
a := x
b := y
// ...
}
大多数人按照“直观”的理解可能会觉得执行 a := x
会比 b := y
代价更高,因为切片 x
比 y
大得多。其实 x
和 y
的数据类型都是 []byte
,在 Go 内部每个切片实际存储的数据是三个 uintptr
的长度,所以对 a
和 b
的赋值需要的代价是一样的,与切片数据的实际长度无关。
需要特别说明的是,在 Go 语言中有两种看起来很像的数据类型:数组(array)和切片(slice)。
数组
数组的长度是固定的,比如 [4]int{}
表示一个长度为 4 的 int
数组,其在内存中的数据结构是这样的:
当将数组赋值给一个变量,实际上会将数组的整块内存区域都复制一份。
切片
切片的长度是不固定的,可以随使用需要动态增长,通过 []int{}
表示一个 int
切片,其在内存中的数据结构是这样的:
第一部分是切片对应的数组指针(ptr),第二部分是切片的长度(len),第三部分是切片的容量(cap)(即所指向的数组的实际长度)。当将切片复制给一个变量,实际上是将切片的 ptr、len、cap 这三项数据复制一份,而数据指向的数组还是原来那个。所以此时修改了切片某个索引位置的数据时,会影响到所有相同 ptr 的切片。
参考链接
- Are large slices more expensive than smaller ones? , Dave Cheney
- Go Slices: usage and internals, The Go Blog