這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Golang slice
yongsean 作者
2017.02.17 00:07 開啟App
建立切片,len、cap、append
b := make([]int, 5)
println(len(b), cap(b)) // 輸出結果是:5, 5
fmt.Println(b) // 輸出結果是:[0 0 0 0 0]
上述代碼是產生預設佔用5個0值的切片,下面的輸出結果是另一回事
b := make([]int, 0, 5)
println(len(b), cap(b)) // 輸出結果是:0, 5
fmt.Println(b) // 輸出結果是:[]
上述代碼是產生cap長度為5,實際使用長度為0的切片,在指定的cap內進行append操作,是不會發生記憶體拷貝擴容操作。
b := make([]int, 0, 2)
fmt.Printf("%d, %d, %p\n", len(b), cap(b), b) // 0, 2, 0xc420012190
b = append(b, 1)
b = append(b, 2)
fmt.Printf("%d, %d, %p\n", len(b), cap(b), b) // 2, 2, 0xc420012190
b = append(b, 3)
fmt.Printf("%d, %d, %p\n", len(b), cap(b), b) // 3, 4, 0xc420012198
最後一行輸出顯示,記憶體位址跟前2次輸出的不一樣,並且cap值也在原來的基礎上,翻了一倍,相當於做了如下操作:
// mock append
tmp := make([]int, 0, cap(b) * 2) // 是當前的cap值的翻倍
// 複製操作略過。。。
b = tmp
copy
正常的切片copy操作
a := make([]int, 5)
a[0] = 1
a[1] = 2
fmt.Println(a) // [1 2 0 0 0]
b := make([]int, 5)
b[0] = 11
b[1] = 22
b[2] = 33
b[3] = 44
b[4] = 55
fmt.Println(b) // [11 22 33 44 55]
copy(b, a)
fmt.Println(b) // [1 2 0 0 0]
另一個效果
a := make([]int, 5)
a[0] = 1
a[1] = 2
fmt.Println(a) // [1 2 0 0 0]
b := make([]int, 0, 5) // len(b)!=cap(b)
b = append(b, 11)
b = append(b, 22)
fmt.Println(b) // [11, 22]
copy(b, a)
fmt.Println(b) // [1 2]
第三行的輸出,只是[1 2],不是[1 2 0 0 0],是對b切片裡len(b)長度內的元素進行對應下標複製操作,假如len(b)==0,那輸出結果是[]。 這是需要小心的地方,老司機會一不留神搞錯,不瞭解的人那就更難說。
切片的切片
a := make([]int, 5)
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
// 未經處理資料輸出
fmt.Printf("%v, %p\n", a, a) // [1 2 3 4 5], 0xc4200141b0
aslice1 := a[1:]
// 第一組輸出
fmt.Printf("%v, %p\n", aslice1, aslice1) // [2 3 4 5], 0xc4200141b8
fmt.Println(len(aslice1), cap(aslice1)) // 4 4
aslice2 := a[1:3]
// 第二組輸出
fmt.Printf("%v, %p\n", aslice2, aslice2) // [2 3], 0xc4200141b8
fmt.Println(len(aslice2), cap(aslice2)) // 2 4
aslice3 := a[:3]
// 第三組輸出
fmt.Printf("%v, %p\n", aslice3, aslice3) // [1 2 3], 0xc4200141b0
fmt.Println(len(aslice3), cap(aslice3)) // 3 5
上面幾組輸出,粗看沒什麼,細看還是有值得注意的
每組的len和cap值都不太一樣
切片的記憶體位址不是完全相同
len的值好理解,無異議。
cap的值:
在 第一組輸出 中是4,是新切片首地址到原始切片尾地址的個數。
在 第二組輸出 中也是4,是新切片首地址到原始切片尾地址的個數
在 第三組輸出 中是5,道理如上
記憶體位址:
未經處理資料輸出的地址和第三組輸出的地址一樣
第一組輸出的地址和第二組輸出的地址一樣
這樣輸出的原因是,指向的切片首地址一樣。在64位作業系統,int類型佔8個位元組,第二組輸出的地址比第三組輸出的地址多8個數值。若有新的切片是如下定義:
aslice4 := a[2:]
fmt.Println("%p\n", aslice4) // 0xc4200141c0
那輸出結果是:0xc4200141c0,是 0xc4200141b8+8 的結果