切片不是数组,他是指向底层的数组
创建一般用make方法,第一个参数是指向的数组类型,第二个是存放元素的个数,第三个是存放容量,如果存放的个数超过容量,那么他就会从分配内存地址(容量在原来的基础上*2),len方法获取长度,cap获取容量
如果是简洁声明,不声明数组长度就是切片,譬如
s:=[] int{}//切片
a:=[10] int{1,2,3,4,5,6,7,8,9,10}//数组
c:=a[3,5]//切片c获取数组a下标3到5位元素值,包括3不包括5下标
d:=a[3:]//下标3到a数组的长度
slice:= make(int[], 4, 6) 创建一个长度为4,容量为6的int类型slice
package mainimport "fmt"
func testModifyElem(s []int) {s[0] = 1fmt.Printf("s inter address is %p \n", s)fmt.Println("s=", s)
}func main() {fmt.Println("------------------------ slice0 -------------------------")sa := make([]int, 10)fmt.Printf("sa address is %p \n", sa)fmt.Println("sa=", sa)testModifyElem(sa)fmt.Printf("post sa address is %p \n", sa)fmt.Println("post sa=", sa)
}
------------------------ slice0 -------------------------
sa address is 0xc0001220f0
sa= [0 0 0 0 0 0 0 0 0 0]
s inter address is 0xc0001220f0
s= [1 0 0 0 0 0 0 0 0 0]
post sa address is 0xc0001220f0
post sa= [1 0 0 0 0 0 0 0 0 0]
函数内部对slice某个元素的修改,会影响函数外面的slice。
函数内部的slice地址和函数外部的slice是一致的。
package mainimport "fmt"func testModifyElem2(s []int) {s[0] = 1fmt.Printf("函数内部 s inter address is %p \n", s)fmt.Println("函数内部 s=", s)s = append(s, 11)fmt.Printf("函数内部 s inter address(after append) is %p \n", s)fmt.Println("函数内部 s=(after append)", s)
}func main() {fmt.Println("------------------------ slice0 -------------------------")sa := make([]int, 10)fmt.Printf("调用前 sa address is %p \n", sa)fmt.Println("调用前 sa=", sa)testModifyElem2(sa)fmt.Printf("调用后 post sa address is %p \n", sa)fmt.Println("调用后 post sa=", sa)
}
------------------------ slice0 -------------------------
调用前 sa address is 0xc0000a60f0
调用前 sa= [0 0 0 0 0 0 0 0 0 0]
函数内部 s inter address is 0xc0000a60f0
函数内部 s= [1 0 0 0 0 0 0 0 0 0]
函数内部 s inter address(after append) is 0xc0000d6000
函数内部 s=(after append) [1 0 0 0 0 0 0 0 0 0 11]
调用后 post sa address is 0xc0000a60f0
调用后 post sa= [1 0 0 0 0 0 0 0 0 0]
函数内部对slice某个元素的修改,会影响函数外面的slice。
如果函数内部对slice的append操作,不会影响到函数外部slice。
我们的slice len和cap都是10,函数内部的append会导致扩容,扩容之后,地址发生了变化,但是函数外部的slice地址并没有变化。这说明了golang的slice传递slice不是引用传递,而是值传递。
扩容策略
func main(){fmt.Println("------------------------ slice9 -------------------------")slice9 := []int{10, 20, 30, 40}newslice9 := append(slice9, 50)fmt.Printf("Before slice = %v, Pointer = %p, len = %d, cap = %d\n", slice9, slice9, len(slice9), cap(slice9))fmt.Printf("Before newSlice = %v, Pointer = %p, len = %d, cap = %d\n", newslice9, newslice9, len(newslice9), cap(newslice9))newslice9[1] += 10fmt.Printf("After slice = %v, Pointer = %p, len = %d, cap = %d\n", slice9, slice9, len(slice9), cap(slice9))fmt.Printf("After newSlice = %v, Pointer = %p, len = %d, cap = %d\n", newslice9, newslice9, len(newslice9), cap(newslice9))}
输出 结果是:
------------------------ slice9 -------------------------
Before slice = [10 20 30 40], Pointer = 0xc000016100, len = 4, cap = 4
Before newSlice = [10 20 30 40 50], Pointer = 0xc00001c0c0, len = 5, cap = 8
After slice = [10 20 30 40], Pointer = 0xc000016100, len = 4, cap = 4
After newSlice = [10 30 30 40 50], Pointer = 0xc00001c0c0, len = 5, cap = 8
Go 中切片扩容的策略是这样的:
首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)
否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap)
否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)
注意:扩容扩大的容量都是针对原来的容量而言的,而不是针对原来数组的长度而言的。