我正在閱讀這篇文章,它解釋了 Go 中的切片是如何在后臺實作的:https : //medium.com/swlh/golang-tips-why-pointers-to-slices-are-useful-and-how-ignoring-them -can-lead-to-tricky-bugs-cac90f72e77b
文末附上一段 Go 代碼:
func main() {
slice:= make([]string, 1, 3)
func(slice []string){
slice=slice[1:3]
slice[0]="b"
slice[1]="b"
fmt.Print(len(slice))
fmt.Print(slice)
}(slice)
fmt.Print(len(slice))
fmt.Print(slice)
}
我的第一個猜測是這會列印:
2 [b b]3 [ b b]
實際上它列印:
2[b b]1[]
這表明當匿名函式通過對作為引數傳遞給它的切片進行切片來創建一個新的本地切片時,會為該切片分配一個新的底層陣列。我已經用這個修改后的代碼版本確認了這一點:
func main() {
slice := make([]string, 1, 3)
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Printf("adress of underlying array in main: %p\n", unsafe.Pointer(hdr.Data))
func(slice []string) {
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Printf("adress of underlying array in func before slicing: %p\n", unsafe.Pointer(hdr.Data))
slice = slice[1:3]
slice[0] = "b"
slice[1] = "b"
hdr = (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Printf("adress of underlying array in func after slicing: %p\n", unsafe.Pointer(hdr.Data))
fmt.Print(len(slice))
fmt.Println(slice)
}(slice)
fmt.Print(len(slice))
fmt.Println(slice)
}
哪個列印:
adress of underlying array in main: 0xc0000121b0
adress of underlying array in func before slicing: 0xc0000121b0
adress of underlying array in func after slicing: 0xc0000121c0
2[b b]
1[]
我的問題是:為什么匿名函式中的切片操作會導致分配一個新陣列?我的理解是,如果在 main 中我們正在創建一個容量為 3 的切片,則會創建一個長度為 3 的底層陣列,并且當匿名函式正在操作該切片時,它正在操作相同的底層陣列。
uj5u.com熱心網友回復:
正如 mkopriva 所提到的,重新切片不會重新分配任何東西。僅當附加新值超出切片的容量( source )時才會發生重新分配。
您得到的輸出是因為您在切片中“能夠看到”的元素(包括列印時)取決于其長度:
元素的數量稱為切片的長度,永遠不會為負數。[...] 可以通過內置函式發現切片 s 的長度
len;
用slice := make([]string, 1, 3),構造的原始切片的長度為 1,因此當您列印它時,輸出將是支持陣列位置的一個元素0,它是一個空字串。
使用此代碼:
slice = slice[1:3]
slice[0] = "b"
slice[1] = "b"
您正在有效地改變位置1和2支持陣列的元素,這些元素都不是原始切片的元素。
如果您將原始切片重新切片到容量 - 從而擴展其length,它將列印您期望的內容:
slice = slice[:cap(slice)]
fmt.Println(len(slice)) // 3
fmt.Println(slice) // [ b b]
// ^ first is empty string
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/393688.html
