前面有文章写了数组和切片的区别,一直有点一知半解,今天再来理一下思路。
数组和切片最明显的区别
最明显的区别就是声明上的区别, 数组的声明带上了len, 而切片不需要声明长度,运行时修改长度
var array [2]int // 数组声明
var array [2]int = [2]int{1, 2} //数组声明加初始化
var slice []int //切片
slice := make([]int, 6) // 长度为6的切片
数组是值传递指针传递?
在go中,于C数组变量隐式作为指针传递不同,go数组是值类型,赋值和函数传参操作都会复制整个数组数据
func main() {
arrayA := [2]int{100, 200}
var arrayB [2]int
arrayB = arrayA
fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
fmt.Printf("arrayB : %p , %v\n", &arrayB, arrayB)
testArray(arrayA)
}
func testArray(x [2]int) {
fmt.Printf("func Array : %p , %v\n", &x, x)
}
// arrayA : 0xc4200bebf0 , [100 200]
// arrayB : 0xc4200bec00 , [100 200]
// func Array : 0xc4200bec30 , [100 200]
这会带来什么问题呢?假想每次传参都用数组,那么每次数组都要被复制一遍,非常消耗内存。这就是就需要指针传递了,函数直接传递一个8字节的指针就可以了,非常省内存,切片是引用类型,在go中是没有引用传递的,只有值传递。
切片的数据结构
type slice struct {
array unsafe.Pointer
len int
cap int
}
![image.png](https://cdn.nlark.com/yuque/0/2021/png/2332713/1624802912511-3a6172e2-4ade-4365-a49b-687066f794ea.png#clientId=u2e86e4cf-c50e-4&from=paste&height=276&id=u099c3dd6&margin=%5Bobject%20Object%5D&name=image.png&originHeight=458&originWidth=996&originalType=binary&ratio=2&size=21248&status=done&style=none&taskId=ub23ad90a-89e4-4b2f-8274-32f46248e50&width=600)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2332713/1624803405764-9f57564b-9d19-4a2a-a713-864fd260307b.png#clientId=u2e86e4cf-c50e-4&from=paste&height=401&id=ud71735ba&margin=%5Bobject%20Object%5D&name=image.png&originHeight=802&originWidth=1730&originalType=binary&ratio=2&size=70774&status=done&style=none&taskId=uf3e4d3d4-69c5-4e71-8236-41b18eff5b5&width=865)
创建切片
切片的扩容策略
slice := []int{10, 20, 30, 40}
newSlice := append(slice, 50)
这个扩容策略,新的切片和之前的切片已经不同了,新切片指向的数组是一个全新的数组。并且 cap 容量也发生了变化。
切片扩容的bug
但是切片扩容有一个很bug的地方,那就是不一定会产生新的数组,比如说我们append的时候数组还有富余的量,就不会产生一个新的数组。假如现在很多切片执行这个数组,改动数组中的一个元素,全部切片都会修改。我们可以使用深拷贝避免这个bug。
深拷贝
深拷贝确保是新的数组,新的切片。修改动作不会对别的切片产生影响。
举个最简单的例子
func sliceOperator1(slice []int) {
slice = append(slice, 5)
fmt.Println(slice)
fmt.Printf("sliceOperator1 %p\n", &slice[0])
ret = append(ret, slice)
}
func sliceOperator2(slice []int) {
fmt.Printf("sliceOperator2 %p\n", &slice[0])
fmt.Println(slice)
slice = append(slice, 2)
fmt.Println(slice)
}
var ret [][]int
func main() {
ret = [][]int{}
n := 6
arr := make([]int, n)
fmt.Printf("%p\n", &arr[0])
arr = append(arr, 7)
fmt.Printf("%p\n", &arr[0])
sliceOperator1(arr)
fmt.Println(arr)
sliceOperator2(arr)
fmt.Println(arr)
fmt.Println(ret)
}