表面上看,slice作为参数的时候传递的是引用,在函数中对slice的修改在函数返回以后才能看到。但是其实,golang里面所有的参数传递都是传值,slice也不例外,只不过slice的值是一个header,slice传值传的是SliceHeader:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
其中Data为一个指针,指向slice对应的数组,Len记录数组的大小,Cap记录数组实际占用的内存空间。当slice作为参数调用函数的时候,实参slice和形参slice的Data指向相同的内存区域。但是,如果在函数中修改了slice的长度,这个修改是不会被记录的。例如:
func main(){
s:=make([]int,1,1)
f(s)
fmt.Println(len(s))
}
func f(x []byte){
x=append(x,1)
}
在main函数中,s的Data初始指向一个数组,Len和Cap也为1。
调用f的时候,s的SliceHeader传递给x,x的Data和s的Data指向同一内存区域,x的Len和Cap为1
这个时候,向x添加一个元素,系统会重新分配内存存储新数组,x的Data指向新数组,xLen和Cap变成2,但是s的指针还指向原来的内存区域,并且Cap和Len仍然为1,此时如果引用第二个元素s[1]会报index out of range 的错误。
ps:个人推测golang里面map应该也可能存在类似的问题,不过我现在还没有验证。