// slice 数据结构
type slice struct {
array unsafe.Pointer
len int
cap int
}
func main() {
array := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Printf("%p\n", &array) // 0xc00011c000
s1 := array[:5]
s2 := array[3:]
fmt.Printf("%p s1 =%v\n", s1, s1) // 0xc00011c000 [1 2 3 4 5]
fmt.Printf("%p s2 =%+v\n", s2, s2) // 0xc00011c018 [4 5 6 7 8 9 0]
s2[0] = 0
fmt.Printf("%p s1 =%v\n", s1, s1) // 0xc00011c000 [1 2 3 0 5]
fmt.Printf("%p s2 =%v\n", s2, s2) // 0xc00011c018 [0 5 6 7 8 9 0]
fmt.Printf("%p array =%v\n", &array, array) // 0xc00011c000 [1 2 3 0 5 6 7 8 9 0]
}
可以看到s1 := array[:5]
与原数组的地址相同,s2 := array[3:]
与原数组地址不同,但修改s2时,同时修改到了s1和原数组,因为这3者的底层数组是同一个,即原array,s2地址不同的原因是因为它是从下标为3开始的。
扩容
func appendSli(sli []int) {
// 0xc000090000 0xc00000c060 [2]
fmt.Printf("slice address in appendSli1:%p %p %+v\n", sli, &sli, sli)
sli[0] = 4
sli = append(sli, 3)
// 0xc000090030 0xc00000c060 [4 3]
// sli发生了扩容->sli有了新的底层数组->%p sli的地址变了
// 但此时&sli并没有变化, 因为sli变量还是不变的(slice是个struct), 变的是底层数组
fmt.Printf("slice address in appendSli1:%p %p %+v\n", sli, &sli, sli)
// 这里再次修改sli的值, 已经不能影响到外部sli变量了
// 因为此时本函数内部的sli变量对应的底层数组和main的sli变量对应的底层数组已经不是同一个数组了
sli[0] = 5
}
func main() {
s := make([]int, 0, 1)
s = append(s, 1)
fmt.Printf("%p %p %+v\n", s, &s, s) // 0xc000090000 0xc00008e000 [1]
s[0] = 2
appendSli(s)
fmt.Printf("%p %p %+v\n", s, &s, s) // 0xc000090000 0xc00008e000 [4]
}
fmt特殊处理了slice类型, 以%p
打印slice
其实打印的是该slice变量对应的底层数组的首元素的地址
以%p
打印&slice
打印的是该slice
(结构体)变量的地址
在调用函数时,会开辟一个新的内存空间做值复制,由于这个struct有array这个指针, 所以slice副本也指向了同一个array,所以也可以做到修改外部变量的值。
但是这个副本的len和cap在函数内部改动之后,外部变量是不变的, 因为它本身是个新的slice变量, len和cap字段是独立的。
可以通过函数修改存储元素的内容,但是永远修改不了len
和cap
,因为他们只是一个拷贝,如果要修改,那就要传递*slice
作为参数才可以。
slice会在容量cap不足时扩容,开辟新的array空间,把旧的复制过去。
nil vs empty slice
// nil slice
var slice []string
slice == nil // true
// Empty slice
var slice = []string{}
// or
var slice = make([]string, 0)
slice == nil // false
nil 切片是在未初始化的情况下声明的切片。它的长度和容量为 0,没有底层数组,数组的零值为 nil。
空切片是包含零个元素的切片。它有底层数组,但元素个数为零。
在大多数情况下,nil 切片和空切片之间没有明显的区别。内置函数 append、len 和 cap 对两者都返回相同的结果,并且可以使用 for...range(0 次迭代)。
但是,在json序列化时有区别,nil slice 会被序列化为 null
,而 empty slice 被序列化为[]
。