亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Go什么時候分配一個新的后備數組給slice?

Go什么時候分配一個新的后備數組給slice?

Go
手掌心 2023-06-12 16:52:24
在閱讀 Go 切片時,我在方法的上下文中遇到了這種append行為如果 s 的后備數組太小而無法容納所有給定值,則會分配一個更大的數組。返回的切片將指向新分配的數組。為了理解這一點,我編寫了以下代碼:在 Go Playground 上試用func makeSlices() {? ? var a []int;? ? a = append(a, 0)? ? b := append(a, 1)? ? printSlice("b", b)? ? c := append(a, 2)? ? printSlice("b", b)? ? printSlice("c", c)}func printSlice(name string, s []int) {? ? fmt.Printf("var=%v len=%d cap=%d first_address=%v %v\n", name, len(s), cap(s), &s[0], s)}輸出:var=b len=2 cap=2 first_address=0x414020 [0 1]var=b len=2 cap=2 first_address=0x414020 [0 2]var=c len=2 cap=2 first_address=0x414020 [0 2]我希望b并c指向相同的底層數組,因為它們都是相同長度的切片但是如果我要為另一個長度的切片改變相同的代碼:在 Go Playground 上試用func makeSlices() {? ? var a []int;? ? a = append(a, 0, 9)? ? d := append(a, 1, 2)? ? printSlice("d", d)? ? e := append(a, 3, 4)? ? printSlice("d", d)? ? printSlice("e", e)}輸出:var=d len=5 cap=8 first_address=0x450020 [0 0 9 1 2]var=d len=5 cap=8 first_address=0x450020 [0 0 9 1 2]var=e len=5 cap=8 first_address=0x450040 [0 0 9 3 4]在這種情況下,d和e應該指向相同的后備數組,因為它們又是相同長度的切片,但它們不是。為什么會出現這種行為異常?Go 到底什么時候決定為切片分配一個新的支持數組?
查看完整描述

2 回答

?
qq_花開花謝_0

TA貢獻1835條經驗 獲得超7個贊

答案很簡單:append()如果要追加的元素不適合當前容量,則分配一個新的后備數組(并復制當前內容)。正式地:


if len(s) + len(newElements) > cap(s) {

    // Allocate new backing array

    // copy content (s) over to new array

} else {

    // Just resize existing slice

}

// append (copy) newElements

因此,例如,如果 len=2,cap=4,您可以附加 2 個元素,無需分配。


如果len=2,cap=4,追加3個元素,則len+3 > cap,那么會分配一個新的backing array(容量會大于len+3,考慮到未來的增長,但它的長度會為 2+3=5)。


解釋你的第一個例子

在您的第一個示例中,您聲明了一個長度和容量為 0 的切片變量。


var a []int

fmt.Println(len(a), cap(a)) // Prints 0 0

當您執行第一個追加時,將分配一個新數組:


a = append(a, 0)

fmt.Println(len(a), cap(a)) // Prints 1 2

當你做另一個追加時,它適合容量,所以沒有分配:


fmt.Println(len(a), cap(a)) // Prints 1 2

b := append(a, 1)

fmt.Println(len(b), cap(b)) // Prints 2 2

但這次您將結果切片存儲在 中b,而不是a. 因此,如果您對 進行第 3 次追加a,它仍然具有 length=1 和 cap=2,因此將另一個元素追加到a不需要分配:


fmt.Println(len(a), cap(a)) // Prints 1 2

c := append(a, 2)

fmt.Println(len(c), cap(c)) // Prints 2 2

所以除了第一個附加,所有其他附加不需要分配,因此第一個分配的后備數組用于所有a,b和c切片,因此它們的第一個元素的地址將是相同的。這就是你所看到的。


解釋你的第二個例子

您再次創建一個空切片(len=0,cap=0)。


然后你做第一個追加:2個元素:


a = append(a, 0, 9)

fmt.Println(len(a), cap(a)) // Prints 2 2

這將分配一個長度為 2 的新數組,因此切片的長度和容量都將為 2。


然后你做你的第二個追加:


d := append(a, 1, 2)

fmt.Println(len(d), cap(d)) // Prints 4 4

由于沒有空間容納更多元素,因此分配了一個新數組。但是您將指向這個新數組的切片存儲在 中d,而不是 中a。a仍然指向舊數組。


然后你做你的第三次追加,但是到a(指向舊數組):


fmt.Println(len(a), cap(a)) // Prints 2 2

e := append(a, 3, 4)

fmt.Println(len(e), cap(e)) // Prints 4 4

同樣,array ofa不能容納更多元素,因此分配了一個新數組,您將其存儲在e.


因此d,e具有不同的支持數組,并且附加到與“另一個”切片共享一個支持數組的任何切片不會(不能)改變這個“另一個”切片。所以結果是您d兩次看到相同的地址,而看到不同的地址e。


查看完整回答
反對 回復 2023-06-12
?
慕沐林林

TA貢獻2016條經驗 獲得超9個贊

看第一個printSlice("a", a)。長度為 1,容量為 2。當你添加一個項目時,不需要分配一個更大的底層數組,所以同一個數組用于bc

一旦長度超過 2,(?d := append(c, 3)),就會為 分配一個新的后備數組d。c保持不變。因此,當e創建另一個新的后備陣列時,會發生相同的過程。


查看完整回答
反對 回復 2023-06-12
  • 2 回答
  • 0 關注
  • 195 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號