// grow grows the slice s so that it can hold extra more values, allocating // more capacity if needed. It also returns the old and new slice lengths. funcgrow(s Value, extra int) (Value, int, int) { i0 := s.Len() i1 := i0 + extra if i1 < i0 { panic("reflect.Append: slice overflow") } m := s.Cap() if i1 <= m { return s.Slice(0, i1), i0, i1 } if m == 0 { m = extra } else { for m < i1 { if i0 < 1024 { m += m } else { m += m / 4 } } } t := MakeSlice(s.Type(), i1, m) Copy(t, s) return t, i0, i1 }
首先,得提一下 go 是没有范型支持,在这个前提上为什么 append 可以传入任何类型的 slice 并正确运行呢?
1
funcappend(slice []Type, elems ...Type) []Type
append 的接口定义如上所示,那么 Type 是什么为什么可以接受所有类型呢?是 interface{} 嘛?
其实答案也很简单,并不是。因为 go 没有范型支持,所以 append 是由编译器进行支持的。
其次,想弄明白 append 具体做了什么, 要提到一个小问题:回答下面程序的输出结果。
1 2 3 4 5 6 7 8 9 10
a := make([]int, 5, 10) b := a // fmt.Println(len(a), cap(a), &a[0]) // fmt.Println(len(b), cap(b), &b[0]) a = append(a, 1, 1, 1) b = append(b, 2, 2) fmt.Println(a) fmt.Println(len(a), cap(a)) fmt.Println(b) fmt.Println(len(b), cap(b))
如果你最后搞明白了这个程序的结果,那你就完全明白 slice 和 append 了。
首先, slice 相对于 C 里面的指针,其多了两个功能:当前数组的长度和最大容量。 第一步,用 slice a 初始化 b,此时 b 的 len、 cap 和指针都和 a 相同。 第二步,对 slice a 进行 append,底层数组的第5-7位(从0计数)被填充对应的值。此时 slice b 三个数值没有任何变化,即还是指向了和 slice a 一样的空间。 第三步,对 slice b 进行 append,底层数组的第5-6位被填充对应的值。
此时 slice a 和 b 的底层数组的指针还是一样的,即指向了同一片空间。所以输出结果乍一看有点反常理,其实理解了 slice 的机制也很容易想明白。