切片数据结构
// runtime/slice.go
type slice struct {
array unsafe.Pointer // 元素指针
len int // 长度
cap int // 容量
}
切片的内存模型
func main() {
arr := [5]int{1, 3, 5, 6, 7}
fmt.Printf("addr:%p\n", &arr)// addr:0xc42001a1e0
s1 := arr[:]
fmt.Printf("addr:%p\n", &s1)// addr:0xc42000a060
changeSlice(s1)
}
func changeSlice(s []int) {
fmt.Printf("addr:%p\n", &s)// addr:0xc42000a080
fmt.Printf("addr:%p\n", &s[0])// addr:0xc42001a1e0
}
代码中定义了一个数组 arr,然后用它生成了一个slice。如果go中存在引用传递,形参 s 的地址应该与实参 s1 一样(上面c++的证明),通过实际的情况我们发现它们具备完全不同的地址,也就是传参依然发生了拷贝——值传递。
但是这里有个奇怪的现象,大家看到了 arr 的地址与 s[0] 有相同的地址,这也就是为什么我们在函数内部能够修改 slice 的原因,因为当它作为参数传入函数时,虽然 slice 本身是值拷贝,但是它内部引用了对应数组的结构,因此 s[0] 就是 arr[0] 的引用,这也就是能够进行修改的原因。