Slice的本质
Slice的本質(zhì)
我們先看下面的代碼,看看它的輸出是什么:
package mainimport "fmt"type Slice []intfunc (A Slice) Append(value int) {A = append(A, value) } func main() {mSlice := make(Slice, 10, 20)mSlice.Append(5)fmt.Println(mSlice) } //output: [0 0 0 0 0 0 0 0 0 0]我們查看append()前后變量的地址值:
func (A Slice) Append(value int) {A1 := append(A, value)fmt.Printf("&A = %p &A1 = %p\n", A, A1) } //output: &A = 0xc000114000 &A1 = 0xc000114000所以說append()之后返回的Slice,是不是原來的Slice?
其實,我們在make一個Slice的時候可以傳遞三個參數(shù):數(shù)據(jù)、長度和容量,這里就要提到SliceHeader。
SliceHeader是Slice運行時的具體表現(xiàn),它的結(jié)構(gòu)定義如下:
type SliceHeader struct {Data uintptr //指向底層數(shù)據(jù)源數(shù)組Len intCap int }那么我們可以將Slice轉(zhuǎn)換為SliceHeader,再來看看A和A1內(nèi)部的值是否一致。
func (A Slice) Append(value int) {A1 := append(A, value)sh := (*reflect.SliceHeader)(unsafe.Pointer(&A))fmt.Printf("A Data:%d,Len:%d,Cap:%d\n", sh.Data, sh.Len, sh.Cap)sh1 := (*reflect.SliceHeader)(unsafe.Pointer(&A1))fmt.Printf("A1 Data:%d,Len:%d,Cap:%d\n", sh1.Data, sh1.Len, sh1.Cap) } //output: A Data:824634474496,Len:10,Cap:20 // A1 Data:824634474496,Len:11,Cap:20會發(fā)現(xiàn)它們的Len是不一樣的,所以不是同一個Slice,因此使用append()并沒有改變原來的A,而是生成了一個新的A1,A只在Append()內(nèi)有效,mSlice并沒有發(fā)生改變。這里正確的做法是讓Append()返回append()之后的結(jié)果。
Append returns the updated slice. It is therefore necessary to store the result of append
上面的例子中,設(shè)置的Len是10和 Cap 是20,Cap足夠大,所以內(nèi)置的append()并沒有生成新的底層數(shù)組。
mSlice := make(Slice, 10, 10)再運行后發(fā)現(xiàn)兩個Slice的Data不再一樣:
//output: A Data:824633852064,Len:10,Cap:10 // A1 Data:824634187776,Len:11,Cap:20這是因為在append()的時候,發(fā)現(xiàn)Cap不夠,生成了一個新的Data數(shù)組,用于存儲新的數(shù)據(jù),并且同時擴充了Cap容量。
總結(jié)
- 上一篇: Go unsafe Pointer
- 下一篇: 亚马逊云科技大中华区新任总裁储瑞松首亮相