这几天刷题时,总是被切片和append坑了,重新看书后,发现是自己当时对他们认识不到位,重新补充下。
从代码分析:
var arr1 = [3]int{1,2,3} //定义一个数组,需要在初始化时指定长度 var arr2 = arr1 //将一个数组赋值给另一个数组时,传递的是一份拷贝 arr2[1] = -2 fmt.Println(arr1,arr2) //[1 2 3] [1 -2 3] // 不指定长度 则为切片 var sli1 = []int{1,2,3} sli2 := sli1 // 切片是引用类型,切片包装的数组称为该切片的底层数组。 sli2[1] = -2 fmt.Println(sli1,sli2) //[1 -2 3] [1 -2 3]
总结:数组是值类型 切片是引用类型
那么当函数参数传切片的情况, 也一定是引用传递咯?其实不然
func main(){ xyz := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} rmLast(xyz) fmt.Printf("%v", xyz) //[1 2 3 4 5 6 7 8 9] rmLast1(&xyz) fmt.Printf("%v", xyz) //[1 2 3 4 5 6 7 8] } func rmLast(a []int) { //slice作为函数参数传入函数时,实际上也是拷贝了一份slice a = a[:len(a)-1] } func rmLast1(a *[]int) { *a = (*a)[:len(*a)-1] }
再看append,通常使用append如下:
sli4 := append(sli3,6,7)
我理所当然的认为,既然append的返回值赋给了新元素,当然不会改变sli3的值,其实不然,准确的说不一定,看两段代码:
//append是一个内置函数,用来在指定的slice后面添加1个或多个元素,并且返回一个新的slice。 var arr3 [5]int = [5]int{1,2,3,4,5} sli3 := arr3[:2] // slice3仍然指向arr3 //如果slice的底层数组有足够的空间,那么append函数被调用以后,并不会为新返回的slice分配一个新的数组,而是使用原来的数组。 //这样的话,当改动了新返回的slice以后,原数组也会发生相应的改变。 sli3 = append(sli3,6,7) fmt.Println(arr3,sli3) // [1 2 6 7 5] [1 2 6 7] var arr4 [5]int = [5]int{1,2,3,4,5} sli4 := arr3[:2] // slice3仍然指向arr3 //如果原数组中没有足够的空间的情况下,就会分配一个新的数组,那么再对slice做的任何改变,都与原数组没有关系了 sli4 = append(sli4,6,7,8,9,10) fmt.Println(arr4,sli4) // [1 2 3 4 5] [1 2 6 7 8 9 10]
总结:如果append以后的元素个数已经超过了切片的容量(即切片的底层数组的容量),就会分配一个新的slice