Slice的本质
我们先看下面的代码,看看它的输出是什么:
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
的时候可以传递三个参数:数据、长度和容量,这里就要提到SliceHeader
。
SliceHeader
是Slice
运行时的具体表现,它的结构定义如下:
type SliceHeader struct {Data uintptr //指向底层数据源数组Len intCap int
}
那么我们可以将Slice
转换为SliceHeader
,再来看看A和A1内部的值是否一致。
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
会发现它们的Len
是不一样的,所以不是同一个Slice
,因此使用append()
并没有改变原来的A
,而是生成了一个新的A1
,A
只在Append()内有效,mSlice
并没有发生改变。这里正确的做法是让Append()
返回append()
之后的结果。
Append returns the updated slice. It is therefore necessary to store the result of append
上面的例子中,设置的Len
是10和 Cap
是20,Cap
足够大,所以内置的append()
并没有生成新的底层数组。
mSlice := make(Slice, 10, 10)
再运行后发现两个Slice
的Data
不再一样:
//output: A Data:824633852064,Len:10,Cap:10
// A1 Data:824634187776,Len:11,Cap:20
这是因为在append()
的时候,发现Cap
不够,生成了一个新的Data
数组,用于存储新的数据,并且同时扩充了Cap
容量。