a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
s := a[0:]
s = append(s, 11, 22, 33)
sa := a[2:7]
sb := sa[3:5]
fmt.Println(a, len(a), cap(a)) //输出:[1 2 3 4 5 6 7 8 9 0] 10 10
fmt.Println(s, len(s), cap(s)) //输出:[1 2 3 4 5 6 7 8 9 0 11 22 33] 13 20
fmt.Println(sa, len(sa), cap(sa)) //输出:[3 4 5 6 7] 5 8
fmt.Println(sb, len(sb), cap(sb)) //输出:[6 7] 2 5
可以看出,数组的len和cap是永远相等的,并且是在定义的时候就已经指定的,不能改变。切片s引用这个数组的全部元素,初始长度和容量都为10,继续追加3个元素后,其长度变为13容量为20,。切片sa截取下标2到7的数组片段,长度为5,容量为8,这个容量的改变规则为原容量值减掉起始下标,此时若追加元素,会覆盖掉原内存地址中存在的值。切片sb截取切片sa下标3到5的数组片段,注意,这里的下标指的是sa的下标,不是源数组的下标,长度为2,容量为8-3=5。
3.Slice是引用类型
上边已经提到过,Slice是对源数组的一个引用,改变Slice中的元素的值,实质上就是改变源数组的元素的值。
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
sa := a[2:7]
sa = append(sa, 100)
sb := sa[3:8]
sb[0] = 99
fmt.Println(a) //输出:[1 2 3 4 5 99 7 100 9 0]
fmt.Println(sa) //输出:[3 4 5 99 7 100]
fmt.Println(sb) //输出:[99 7 100 9 0]
可以看到,不管是append操作,还是赋值操作,都影响了源数组或者其他引用同一数组的Slice的元素。Slice进行数组引用的时候,其实是将指针指向了内存中具体元素的地址,如数组的内存地址,事实上是数组中第一个元素的内存地址,Slice也是如此。
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
sa := a[2:7]
sb := sa[3:8]
fmt.Printf("%pn", sa) //输出:0xc084004290
fmt.Println(&a[2], &sa[0]) //输出:0xc084004290 0xc084004290
fmt.Printf("%pn", sb) //输出:0xc0840042a8
fmt.Println(&a[5], &sb[0]) //输出:0xc0840042a8 0xc0840042a8
4.Slice引用传递发生“意外”
上边我们一直在说,Slice是引用类型,指向的都是内存中的同一块内存,不过在实际应用中,有的时候却会发生“意外”,这种情况只有在像切片append元素的时候出现,Slice的处理机制是这样的,当Slice的容量还有空闲的时候,append进来的元素会直接使用空闲的容量空间,但是一旦append进来的元素个数超过了原来指定容量值的时候,内存管理器就是重新开辟一个更大的内存空间,用于存储多出来的元素,并且会将原来的元素复制一份,放到这块新开辟的内存空间。










