数组
数组的声明
数组的长度是固定的,声明的时候就需要确定,长度也是数组类型的一部分,所以两个长度不同的int数组也算是两个不同的类型。
声明方法:
var nums [5]int
var nums = [5]int{}
var nums = [5]int{1, 2, 3} //前三个元素初始化,剩下的元素为0
nums := [...]int{1, 2, 3} //也可以让程序计数,但是[]内不能空着,空着就会声明成切片slice
nums := [5]int{3:5} //将nums[3]初始化为5
数组的拷贝
数组是一种值类型而不是引用类型,所以将一个数组赋值给另一个数组时进行了值拷贝,两个数组独立互不影响
func main() {
arr1 := [...]int{1, 2, 3}
arr2 := arr1
arr1[0] = 2
fmt.Println(arr1) // [2 2 3]
fmt.Println(arr2) // [1 2 3]
}
数组作为函数参数
因为数组是值类型,所以传参也会出现值拷贝,在函数中的操作对原数组不影响,如果想要修改原数组,需要&
引用传递
func test1(arr [5]int) {
arr[0] = 5
}
func test2(arr *[5]int) {
arr[0] = 5
}
func main() {
arr1 := [5]int{}
test1(arr1)
fmt.Println(arr1) // [0 0 0 0 0]
test2(&arr1)
fmt.Println(arr1) // [5 0 0 0 0]
}
切片
切片slice是对数组一个连续片段的引用,和数组不同,切片是引用类型,在内存中是一个有3个域的结构体,ptr
是指向底层数组的指针,len
是切片的长度,cap
是切片的容量。
切片的声明
var arr = [5]int{1, 2, 3, 4, 5}
slice1 := arr[0:5] //对已存在的数组进行引用
slice2 := []int{1, 2, 3, 4, 5} //直接创建一个切片
slice3 := make([]int, 5, 10) //用make()创建并初始化一个切片,5是len长度,10是cap容量,cap参数可忽略
slice4 := new([]int) //返回指向slice4的指针,创建并给切片分配了内存,但是ptr为nil指针,没有初始化,len和cap都为0
slice5 := make([]int, 0) //创建并给切片分配了内存,并将ptr初始化指向一个空数组,len和cap都为0
切片的赋值和拷贝
切片是一种引用类型而不是值类型,一个切片赋值给另一个切片时,复制了一份指向同一位置的ptr
,所以会互相影响
func main() {
arr1 := []int{0, 0, 0}
arr2 := arr1
arr1[0] = 2
fmt.Println(arr1) // [2 0 0]
fmt.Println(arr2) // [2 0 0]
}
使用copy()
对切片进行拷贝,将源切片的数据拷贝到目的切片中,是两份指向不同的切片,但不会帮目的切片分配空间
func main() {
arr1 := []int{0, 0, 0}
arr2 := make([]int, 3)
copy(arr2, arr1)
arr1[0] = 2
fmt.Println(arr1) // [2 0 0]
fmt.Println(arr2) // [0 0 0]
}
切片作为函数参数
切片本身就是引用类型,直接作为参数就可以对原数组进行修改,不要再用指针指向切片,如test2()
func test1(arr []int) {
arr[0] = 5
}
//不要这样写:
func test2(arr *[]int) {
(*arr)[0] = 6
}
func main() {
arr1 := []int{0, 0, 0, 0, 0}
test1(arr1)
fmt.Println(arr1) // [5 0 0 0 0]
test2(&arr1)
fmt.Println(arr1) // [6 0 0 0 0]
}