go切片原型
// runtime/slice.go
type slice struct {
array unsafe.Pointer// 引用底层数组的指针
len int// 引用数组元素的长度,使用下标对元素进行访问的时候,下标不能超过len
cap int // 引用数组的容量,cap>=len
}
定义一个名为baseSlice 的切片
baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
底层结构
定义一个新的切片slice1
slice1 := baseSlice[2:5] //[]int{2,3,4}
底层结构
定义一个新的切片slice2
slice2 := slice1[4:7] //[]int{6,7,8}
底层结构
源码
package main
import (
"fmt"
)
func main() {
baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice1 := baseSlice[2:5] //[]int{2,3,4}
//这里slice1 cap是8,底层引用的数组为[]int{2, 3, 4, 5, 6, 7, 8, 9}
//所以slice1 len=3, cap=8
//由于 slice1 cap=8 所以slice1[4:7]并不会发生数组越界
slice2 := slice1[4:7] //[]int{6,7,8}
fmt.Printf("slice1:%v.len:%d.cap:%dn", slice1, len(slice1), cap(slice1)) //slice1:[2 3 4].len:3.cap:8
fmt.Printf("slice2:%v.len:%d.cap:%dn", slice2, len(slice2), cap(slice2)) //slice2:[6 7 8].len:3.cap:4
}
切片是值传递
package main
import (
"fmt"
)
func main() {
baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice1 := baseSlice[2:5] //[]int{2,3,4}
appendSlice(slice1)
fmt.Printf("slice1:%vn", slice1) //slice1:[2 3 4]
fmt.Printf("baseSlice:%vn", baseSlice) //baseSlice:[0 1 2 3 4 10 6 7 8 9]
}
func appendSlice(s []int) { //值传递
s = append(s, 10) //这里会改变底层数组里的值,如果append的元素个数大于底层引用数组的容量,则会触发一次数组copy
fmt.Printf("append slice1:%vn", s) //append slice1:[2 3 4 10]
}
引用传递的假象
如果在调用的方法里,修改了传入切片len内的值,导致底层数组对应的值被修改,从调用方法外看,就像传入的切片值被修改了。
package main
import (
"fmt"
)
func main() {
baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice1 := baseSlice[2:5] //[]int{2,3,4}
appendSlice(slice1)
fmt.Printf("slice1:%vn", slice1) //slice1:[123 3 4],看起来是slice1被修改了,其实是由于底层数组被修改的缘故
fmt.Printf("baseSlice:%vn", baseSlice) //baseSlice:[0 1 123 3 4 5 6 7 8 9]
}
func appendSlice(s []int) { //值传递
// s = append(s, 10)
s[0] = 123 //这里会改变底层数组里的值
fmt.Printf("append slice1:%v. len=%d. cap=%dn", s, len(s), cap(s)) //append slice1:[123 3 4]. len=3. cap=8
}
深度copy
package main
import (
"fmt"
)
func main() {
baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice1 := baseSlice[4:7] //[]int{6,7,8}
fmt.Printf("slice1:%vn", slice1) //slice1:[4 5 6]
//slice 深copy
slice2 := make([]int, 3, 3)
copy(slice2, slice1)
fmt.Printf("slice2:%v. len=%d. cap=%dn", slice2, len(slice2), cap(slice2)) //slice2:[4 5 6]. len=3. cap=3
slice2[0] = 100
fmt.Printf("slice2:%vn", slice2) //slice2:[100 5 6]
fmt.Printf("slice1:%vn", slice1) //slice1:[4 5 6]
fmt.Printf("baseSlice:%vn", baseSlice) //baseSlice:[0 1 2 3 4 5 6 7 8 9]
}
扩容
// runtime/slice.go
//cap 为满足存放数据的最小容量
func growslice(et *_type, old slice, cap int) slice {
...
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {//如果slice len<1024,扩容时容量增长2倍,也就是2个变4个
newcap = doublecap
} else {//如果slice len>=1024,扩容时增加原来容量的四分之一
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
...
}
}
nil slice和empty slice
empty slice 底层数组指针指向实际存在的空数组的地址;
nil slice 底层数组指针指向的是nil;
package main
import (
"fmt"
)
func main() {
var nilSlice []int
emptySlice := make([]int, 0)
if emptySlice == nil {
fmt.Println("emptySlice is nil.")
}
if nilSlice == nil {
fmt.Println("nilSlice is nil.") //输出 nilSlice is nil.
}
}
tips:
Go 语言的函数参数传递,只有值传递。