Go語言:append函數源碼學習及切片深度拷貝問題

調用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)方法的性能對比》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章