// 指定长度
x:= [4]int{1,2,3,4}
// or
var y = [4]int
// 不指定长度,由元素个数决定长度
x:= [...]int{1,2,3,4}
在Go中,数组的长度是固定的,创建后不可修改。所以一般情况下用切片比较 方便。切片的长度是可变的,比较好用。
// 1 、直接通过元素初始化
x:= []int{1,2,3,4}
//2、 通过数组生成切片
arr:= [...]int{1,2,3,4}
// 表示以arr的下标0-3 生成一个新的切片
x:= arr[0:4] // 等价于 arr[:4] arr[:]
// 3 通过make初始化
x:= make([]int,4,4) // 初始化一个长度为4 ;容量为4的切片
需要注意的是,切片底层是指向一个数组的。参考以下代码
func main() {
x := [...]int{1, 2, 3, 4}
y := x[:]
y[2] = 1
fmt.Println(x, y)// [1 2 1 4] [1 2 1 4]
}
我们可以看到修改切片的值,数组和切片都同样被改变。但是有一种情况是不同的,就是如果我们对切片进行元素追加,此时切片就指向另一个新的底层匿名数组,此时和原数组就没有关系了
如下:
func main() {
x := [...]int{1, 2, 3, 4}
y := x[:]
y = append(y, 5)
y[2] = 222
fmt.Println(x, y) // [1 2 3 4] [1 222 3 4 5]
}
简单总结就是:切片是指向一个底层数组,如果这个底层数组容量不足时,切片会自动扩容,指向另一个新的底层数组,和原来的数组就没有关系
func Test(arr [4]int) {
arr[1] = 888
}
x := [...]int{1, 2, 3, 4}
Test(x) // x: [1,2,3,4] 值传递不会影响到x的值
func Test(arr *[4]int) {
arr[1] = 888
}
x := [...]int{1, 2, 3, 4}
Test(&x) // [1,888,3,4] 传递地址,函数内修改会影响外部
切片有点特殊。切片本质上是一个包含3个属性的结构体。3个属性分别是,切片的长度、切片的容量、一个指针指向底层数组
// y是切片
unsafe.Sizeof(y)// 24
// 类似
type Slice struct {
len int //切片长度
cap int // 切片容量
point *[](int) // 底层数组的指针
}
需要注意的是,我们通过传递切片也是值传递的(切片本身被拷贝),函数内的修改切片是修改切片的数组指针属性指向的底层数组的, 所以对应底层数组也会被修改。所以有一种错觉,怎么切片值传递,切片值出来被修改了呢。
func Test(arr []int) {
arr[1] = 888
}
func main() {
x := [...]int{1, 2, 3, 4}
y := x[:]
Test(y)
fmt.Println(x) // x [1,888,3,4] 看上去是修改了原切片,其实只是函数内部通过拷贝的底层数组的地址修改了对应底层数组的值
}
但是,如果我们在函数内对切片进行追加,此时,我们再去修改,就是修改了另一个底层数组。这时候原切片和原数组,都不会被修改到了。
func Test(arr []int) {
arr[1] = 888
arr = append(arr, 222)
}
func main() {
x := [...]int{1, 2, 3, 4}
y := x[:]
Test(y)
fmt.Println(x, y)// 这里x y 都不会被修改
}
所以,切片作为函数参数传递,也是符合Go语言的函数参数值传递的理念。
切片也是可以地址传递的
func Test(arr *([]int)) {
(*arr) = append(*arr, 222)
}
func main() {
x := [...]int{1, 2, 3, 4}
y := x[:]
Test(&y)
fmt.Println(x, y)// [1 2 3 4] [1 2 3 4 222]
}
有之前的分析,就很好理解了。切片传递的是指针,函数内对切片追加,修改就是修改了原切片的指向底层数组的指针指向,指向我们新生成的指针。所以main函数中, 我们打印切片,发现打印值就是我们修改的值,而原数组也不会被更改了。