?代碼1.0
package mainimport "fmt"func main() {a := make([]int64, 0, 0) // 改為 a := make([]int64, 0, 2) 時執行輸出也都一樣的println(fmt.Sprintf("a: %v", a))// 輸出:a: []solve(a)println(fmt.Sprintf("a: %v", a))// 輸出:a: []
}func solve(currA []int64) {currA = append(currA, 1)println(fmt.Sprintf("currA: %v", currA))// 輸出:currA: [1]
}
在調solve函數時,發生了引用的復制:currA = a,引用指的是這個東西:
// runtime/slice.gotype slice struct {array unsafe.Pointer // 數組指針len int // 長度 注意:append時len是占位的cap int // 容量
}
也就是產生了slice struct的兩個實例——currA和a,其中currA中各項(包含:array指針、len、cap三個字段)的值是從a的各項值復制來的,其中array指針相同則意味著currA和a指向的底層數組是一個,但是后續currA和a會各自維護自己的len和cap。
這也就是為什么【在子函數solve里append后不會影響父函數main里的切片,但是在子函數solve里用下標修改元素值后是會影響父函數main里的切片(下標修改的過程見下面代碼)】的原因。
package mainimport "fmt"func main() {a := make([]int64, 0, 0)a = append(a, 1)println(fmt.Sprintf("a: %v", a))// 輸出:a: []solve(a)println(fmt.Sprintf("a: %v", a))// 輸出:a: [2]
}func solve(currA []int64) {currA[0] = 2println(fmt.Sprintf("currA: %v", currA))// 輸出:currA: [2]
}
?代碼2.0
package mainimport "fmt"func main() {a := make([]int64, 0, 2) // 后續不需要擴容println(fmt.Sprintf("a: %v", a))// 輸出:a: []b := append(a, 1)println(fmt.Sprintf("a: %v, b: %v", a, b))// 輸出:a: [], b: [1]c := append(a, 2)println(fmt.Sprintf("a: %v, b: %v, c: %v", a, b, c))// 輸出:a: [], b: [2], c: [2]
}
總結分析
append時
沒有發生底層數組數據的直接復制;相反,append
?函數返回了一個新的切片值,該值可能與原始切片共享相同的底層數組(在不需要擴容的情況下),但具有不同的長度和(可能)容量。
另外,append時會會根據原接片的長度去追加。例如原切片長度為1(下標=0),則append時會在下標為0+1的地方追加。
詳細分析
當你執行?b := append(a, 1)
?時,這里發生了幾件事情:
-
如果?
append
?操作不需要擴展底層數組(即新元素的添加不會導致切片的容量不足),那么?append
?會直接在底層數組的末尾添加新元素,并返回一個新的切片值。這個新的切片值會包含原始切片的所有元素(在這個例子中是空的,因為沒有元素),加上新追加的元素。重要的是,這個新的切片值會共享原始切片的底層數組(如果可能的話),但它會有自己的長度(現在至少為 1,因為我們添加了一個元素)。 -
在這個特定的例子中,由于?
a
?是空的,且其容量足以容納至少一個額外的元素(因為我們設置了容量為 2),append(a, 1)
?實際上是在?a
?的底層數組的末尾(盡管這個數組目前還是空的)添加了一個元素,并返回了一個新的切片值?b
。這個新的切片值?b
?指向與?a
?相同的底層數組(在這個例子中這個“相同”的底層數組實際上還沒有被使用來存儲任何元素,但它已經為存儲元素做好了準備),但它的長度是 1,因為它現在包含一個元素。 -
然而,重要的是要理解?
a
?和?b
?是兩個不同的切片值。盡管它們可能(在這個例子中確實)共享相同的底層數組,但每個切片值都有自己獨立的長度和容量。當你將?b
?賦值給一個新變量時,你并沒有復制底層數組,而是創建了一個新的切片頭(包含指向底層數組的指針、長度和容量),并將其賦值給該變量。 -
因此,當你說“包含了 a 的所有元素”時,你實際上是在說新切片?
b
?包含了原始切片?a
?在追加操作之前所擁有的所有元素(在這個例子中是空的),并且額外添加了一個新的元素。這并不意味著底層數組的數據被復制了;相反,它意味著新的切片值?b
?指向了與?a
?相同的底層數組(或一個新的、更大的數組,如果追加操作導致了切片的重新分配),但具有不同的長度。
代碼2.1
package mainimport "fmt"func main() {a := make([]int64, 0, 0) // 后續不需要擴容println(fmt.Sprintf("a: %v", a))// 輸出:a: []b := append(a, 1)println(fmt.Sprintf("a: %v, b: %v", a, b))// 輸出:a: [], b: [1]c := append(a, 2)println(fmt.Sprintf("a: %v, b: %v, c: %v", a, b, c))// 輸出:a: [], b: [1], c: [2]
}
如果append時需要擴容,則將原數組的值復制到擴容后的數組。執行到【b := append(a, 1)】和【c := append(a, 2)】時會發生擴容,但擴容不會對a產生任何影響,因為沒有【a?:= append(a, 1或2)】的操作。