【golang】粗淺理解slice
阿新 • • 發佈:2021-02-06
技術標籤:Go
一、slice的結構
type slice struct {
array unsafe.Pointer
len int
cap int
}
從結構可以看出,slice型別的結構體含有三個元素,第一個是指向底層陣列的指標,第二個是當前slice的長度,第三個是slice的容量。其中cap會以倍數方式擴容。
在《go程式設計語言》一書的第五章5.1最後一段話指出
實參是按值傳遞的,所以函式接收到的是每個實參的副本;修改函式的形參變數並不會影響到呼叫者提供的實參。然而,如提供的實參包含引用型別,比如指標、slice、map、函式或者通道,那麼當函式使用形參變數時,就有可能會間接修改實參變數。
那麼,slice作為形參時,是怎麼影響實參變數的呢?
下面看一段程式碼示例以及執行結果
二、程式碼示例
func TestC(t *testing.T) { studentNos := []int{} fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 1) fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 2) fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 3) fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 4) fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 5) fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) falsify(studentNos) fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 6) fmt.Printf("%+v, %[1]p, %d, %d,%p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) } func falsify(studentNos []int) { fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos[0]=100 fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos[4]=300 fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 400) fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 400) fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 400) fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 400) fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos = append(studentNos, 400) fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) studentNos[1]=200 fmt.Printf(".............%+v, %[1]p, %d, %d, %p \n", studentNos, cap(studentNos), len(studentNos), &studentNos) }
執行結果
[], 0x679188, 0, 0,0xc00005e4a0 [1], 0xc000064158, 1, 1,0xc00005e4a0 [1 2], 0xc000064180, 2, 2,0xc00005e4a0 [1 2 3], 0xc0000621a0, 4, 3,0xc00005e4a0 [1 2 3 4], 0xc0000621a0, 4, 4,0xc00005e4a0 [1 2 3 4 5], 0xc00007c0c0, 8, 5,0xc00005e4a0 .............[1 2 3 4 5], 0xc00007c0c0, 8, 5, 0xc00005e580 .............[100 2 3 4 5], 0xc00007c0c0, 8, 5, 0xc00005e580 .............[100 2 3 4 300], 0xc00007c0c0, 8, 5, 0xc00005e580 .............[100 2 3 4 300 400], 0xc00007c0c0, 8, 6, 0xc00005e580 .............[100 2 3 4 300 400 400], 0xc00007c0c0, 8, 7, 0xc00005e580 .............[100 2 3 4 300 400 400 400], 0xc00007c0c0, 8, 8, 0xc00005e580 .............[100 2 3 4 300 400 400 400 400], 0xc000098200, 16, 9, 0xc00005e580 .............[100 2 3 4 300 400 400 400 400 400], 0xc000098200, 16, 10, 0xc00005e580 .............[100 200 3 4 300 400 400 400 400 400], 0xc000098200, 16, 10, 0xc00005e580 [100 2 3 4 300], 0xc00007c0c0, 8, 5,0xc00005e4a0 [100 2 3 4 300 6], 0xc00007c0c0, 8, 6,0xc00005e4a0
結果分析:
從上面的demo我們發現
1、當slice作為函式的引數時,slice會複製一份
2、形參的slice中的指標與呼叫方實參的slice的指標指向同一底層陣列
3、形參中len和cap與實參的len和cap完全剖離
4、當函式對實參的len範圍內的元素進行修改,會影響實參的值。但對len範圍外的元素修改,對實參無影響。
5、注意,當在函式中,slice發生擴容,其指標將指向新的地址,與實參中指標指向不同的底層陣列。此後再對形參的slice中的元素做任何操作,都不會影響實參。
總結:slice作為函式引數使用時需謹慎