先说结论
GO中只有值传递,没有引用传递
所谓值传递,就是实参通过拷贝将自身内容传递给形参。也就是将传递的内容拷贝一份,给函数。所以函数外和函数里对这个参数地址求值,应该是不一样的。
证明如下:
func
可以发现,slice、map、指针在传递过程中,地址都发生了变化。这说明传递的是一份拷贝。
但是我们又发现,在函数里修改slice、map,函数外的值也会改变,这是为什么呢?
// main
这就必须说到slice、map的存储结构了。以slice为例
![1a57226d488700773a2933b5992318b0.png](https://i-blog.csdnimg.cn/blog_migrate/667e145a95c781f3744bfc9ea33e7e07.png)
slice结构主要包括3个部分:data、len、cap。结构中的len和cap分别指示元素数量和容量。append使得len变大,但如果append之后的元素个数大于cap,会引发扩容机制。此时,会重新创建一个容量适合的底层数组。data为指针,指向底层数组。
所以slice本质上指的就是这个结构(data+len+cap,不包括底层的数组)。在参数传递过程中,这个结构拷贝了一份,data指向的还是原来的底层数组。当我们对slice中的元素进行修改时,还是会通过拷贝之后的data,直接对底层数组进行修改。
go 中,slice、map、channel都是引用类型,所以都会有如上的特性。
由于slice在扩容的过程中,会重新创建底层数组,data指向新的数组。那么,我们可以推测,在get函数中如果能触发扩容,那么修改新的底层数组,并不会对main中的slice造成影响。是否这样呢?
func
果然如此!那么就很清楚了。
总结
1、go中函数传递都是值传递
2、slice、map、channel都是引用类型,即便是值传递,结构内部还是指向原来的引用对象,所以函数体内可以直接修改元素。
3、如果slice触发扩容,data会指向新的底层数组,而不指向外部的底层数组了。所以之后再修改slice,不会对外部的slice造成影响。