数组
- 数组是值类型,一个数组变量表示整个数组,而不是指向数组的首元素的指针,这和C语言不同。
- 将数组赋值给另一个数组,或者数组作函数参数传递时,会将数组的全部数据拷贝一份过去而不是传递一个指针(因此用切片代替数组)。
- 数组类型包括长度,即[5]int和[10]int不是一种类型。
切片
切片不是指针类型,切片数据类型是包含指向一个数组片段的指针,和当前数组片段的长度,以及当前数组最大容量的一种复合数据结构。
type slice struct {
array unsafe.Pointer //数组指针
len int //切片长度
cap int //切片容量
}
数组和切片定义上的区别
创建数组
var balance = [2]float32{1.0, 2.0}
balance := [2]float32{1.0, 2.0}
var balance = [...]float32{1.0, 2.0}
balance := [...]float32{1.0, 2.0}
创建切片
slice := make([]float, 2, 2)
slice := []float32{1.0, 2.0} #这和数组的创建非常类似,区别是[]不用指定长度
slice := slice[1:3]
切片的陷阱
切片作函数参数传递或浅拷贝时,之所以改变一个切片的数据会影响另一个切片,是因为两个切片中中包含了指向同一数组片段的指针。
一切看似正常?但是当一个切片发生扩容时,会将当前切片内的数据复制到另一片内存区域,该切片的数组片段的地址发生改变,所以当切片扩容时修改一个切片的数据时不会再影响到另一个切片!此时只能通过传递切片本身的地址来解决。
package main
import (
"fmt"
)
func testSlice(slice []int){
slice = append(slice, 6,7,8,9,10)
fmt.Println("testSlice:", slice)
}
func main() {
slice := []int{1,2,3,4,5}
testSlice(slice)
fmt.Println("main:", slice)
}
#输出结果:
testSlice: [1 2 3 4 5 6 7 8 9 10]
main: [1 2 3 4 5]
package main
import (
"fmt"
)
func testSlice(slice *[]int) {
*slice = append(*slice, 6,7,8,9,10)
fmt.Println("testSlice:", *slice)
}
func main() {
slice := []int{1,2,3,4,5}
testSlice(&slice)
fmt.Println("main:", slice)
}
#输出结果
testSlice: [1 2 3 4 5 6 7 8 9 10]
main: [1 2 3 4 5 6 7 8 9 10]
另外注意对同一个数组进行多次append操作
s := []int{7} //[7] s(len=1,cap=1)
s = append(s, 1) //[7 1] s(len=2,cap=2)
s = append(s, 2) //此时切片s的len=3;cap=4 [7 1 2], s(len=3,cap=4)
x := append(s, 10) //此时是对 切片[7 1 2]的操作 cap是足够的,内存数组为[7 1 2 10],x([7 1 2 10],len=4, cap=4)
y := append(s, 20) //此时任然是对s(len=3,cap=4)操作,cap足够不需要扩容,20覆盖10,内存数组变为[7 1 2 20]
//x,y两个切片底层的数组指针 指向的是同一块数组存储空间 所以上述语句执行完后 x,y的输出是一样的。
//但是注意当cap容量发生变化时,数组指针(地址)改变,例如 y := append(s, 20,30)时x,y的输出就会不一样 [7 1 2 10] [7 1 2 20 30]
fmt.Println(s, x, y)
//输出结果:[7 1 2] [7 1 2 20] [7 1 2 20]