golang中的slice是引用类型,比如下面这段代码:
s1 := []int{1,2,3,4}
s2 := s1
s1[0] = 100
fmt.Printf("s1=%v,s2=%v", s1, s2)
会看到s1和s2都变成了[100 2 3 4],因为它们共享同一个内存地址。
然而......
我们来看看下面这个“坑”
s1 := []int{1,2,3,4}
s2 := s1
s1 = append(s1, 5)
s3 := s1
s1 = append(s1, 6)
s1[0] = 100
fmt.Printf("s1=%v,s2=%v,s3=%v", s1, s2, s3)
返回结果是:s1=[100 2 3 4 5 6], s2=[1 2 3 4], s3=[100 2 3 4 5] 按理说引用传值,s2的第一个元素也应该是100
要解释这个问题,就要谈一下golang中slice的扩容机制。当切片的长度超出cap容量的时候,就会引发切片扩容,每次增加一倍的容量(当容量达到1024以后,每次增加的量大约是之前的25%~35%)。经扩容后的切片会复制到新的内存地址中。
所以在上面的例子中,当s1第一次append时,触发扩容,cap(s1)从4变到8,此时s1和s2已经不是同一个内存地址了。而当s1第二次append时,cap(s1)依旧是8,没有发生扩容,所以s1和s3是同一个内存地址。最终s3的元素跟随s1变动,而s2没有。