2 回答

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。

TA貢獻2016條經驗 獲得超9個贊
看第一個printSlice("a", a)
。長度為 1,容量為 2。當你添加一個項目時,不需要分配一個更大的底層數組,所以同一個數組用于b
和c
。
一旦長度超過 2,(?d := append(c, 3)
),就會為 分配一個新的后備數組d
。c
保持不變。因此,當e
創建另一個新的后備陣列時,會發生相同的過程。
- 2 回答
- 0 關注
- 195 瀏覽
添加回答
舉報