调用append函数时,当原有长度加上新追加的长度如果超过容量则会新建一个数组,新旧切片会指向不同的数组;如果没有超过容量则在原有数组上追加元素,新旧切片会指向相同的数组,这时对其中一个切片的修改会同时影响到另一个切片。其伪代码在如下文件里,而实际上append会在编译时期被当成一个TOKEN直接编译成汇编代码,因此append并不是在运行时调用的一个函数。 另外,切片截取后生成的新切片与原切片始终指向同一个数组。
\src\cmd\compile\internal\gc\walk.go
// expand append(l1, l2...) to
// init {
// s := l1
// n := len(s) + len(l2)
// // Compare as uint so growslice can panic on overflow.
// if uint(n) > uint(cap(s)) {
// s = growslice(s, n)
// }
// s = s[:n]
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
// }
// s
//
验证append之后新旧切片是否指向同一个数组:
package main
import(
"fmt"
)
func print(sl1 []string, sl2 []string){
fmt.Printf("sl1 len=%d cap=%d\n", len(sl1),cap(sl1))
fmt.Printf("sl2 len=%d cap=%d\n", len(sl2),cap(sl2))
fmt.Printf("sl1=%v\n", sl1)
fmt.Printf("sl2=%v\n\n", sl2)
}
func main(){
sl1 := make([]string,3,5)
sl1[0] = "a"
sl1[1] = "b"
sl1[2] = "c"
//sl1追加上"e"之后长度小于sl1容量,无需扩容,sl1,sl2指向相同数组
sl2 := append(sl1,"e")
sl2[0] = "change_by_sl2_1st_time"
print(sl1,sl2)
//sl2追加上"h"之后长度等于sl2容量,无需扩容,sl1,sl2仍然指向相同数组
sl2 = append(sl2,"h")
sl2[0] = "change_by_sl2_2nd_time"
print(sl1,sl2)
//sl2追加上"j"之后长度超过sl2容量,需要扩容,新建数组,sl1,sl2指向不同数组
sl2 = append(sl2,"j")
sl2[0] = "change_by_sl2_3rd_time"
print(sl1,sl2)
//截取后,新旧切片指向同一个数组,但起止索引可能不同。
//这里 sl2[0] 和 sl1[1] 指向同一个数组元素
sl2 = sl1[1:len(sl1)]
sl2[0] = "change_by_sl2_4th_time"
print(sl1,sl2)
}
输出:
sl1 len=3 cap=5
sl2 len=4 cap=5
sl1=[change_by_sl2_1st_time b c]
sl2=[change_by_sl2_1st_time b c e]
sl1 len=3 cap=5
sl2 len=5 cap=5
sl1=[change_by_sl2_2nd_time b c]
sl2=[change_by_sl2_2nd_time b c e h]
sl1 len=3 cap=5
sl2 len=6 cap=10
sl1=[change_by_sl2_2nd_time b c]
sl2=[change_by_sl2_3rd_time b c e h j]
sl1 len=3 cap=5
sl2 len=2 cap=4
sl1=[change_by_sl2_2nd_time change_by_sl2_4th_time c]
sl2=[change_by_sl2_4th_time c]
参考文章:
《Where is the implementation of func append in Go?》
相关文章:
《Go语言:几种深度拷贝(deepcopy)方法的性能对比》
《Go语言切片拷贝时容量对于效率的影响》