調用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?》
相關文章: