前面有文章写了数组和切片的区别,一直有点一知半解,今天再来理一下思路。
数组和切片最明显的区别
最明显的区别就是声明上的区别, 数组的声明带上了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]intarrayB = arrayAfmt.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.Pointerlen intcap int}
<br />
创建切片

切片的扩容策略
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 [][]intfunc main() {ret = [][]int{}n := 6arr := 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)}
