Go切片在函数中的传递
前言
最近在优化之前实习生的代码,在做control
层和db
层之间参数传递的时候,老师提出了切片就是引用类型,那么在使用数据库Find
功能时只需要传model
切片即可,就不需要传切片指针了,但是自己在实验的时候发现直接传切片时无法获取值的,只有传切片指针才行
1 |
|
后对此情况对网上的参考资料引用了大部分,做了少许修改的标注
slice的参数
切片和数组是不一样的,数组是值类型,切片时引用类型,则我们相信在函数中传递地址切片,函数中改变,会直接影响到函数外与之对应的切片,现在做一个实验验证下想法
1 |
|
上面的代码结果验证了我们的想法,但是在函数中打印切片的地址,则会发现和函数外对应的切片地址不一样
1 |
|
此时我们需要先了解下slice的实现
Slice的实现
切片不等同于数组,但他依赖数组实现的,切片是一种复合结构,它由三部分组成的,第一部分是底层数组的指针ptr
,第二部分是切片大小len
,最后是切片容量cap
1 |
|
- 我们看这个例子,有一个5个元素的数组,slice,slice2分别截取了数组的一部分,并且有共同的一部分,我们现在能明确的就是,两个切片共用一个数组,所以一个改变都改变,还有就是两个数组是两个不同的对象,很明显内存地址不同
- 从数组中切一块下来形成切片很好理解,有时候我们用make函数创建切片,实际上golang会在底层创建一个匿名的数组。如果从新的slice再切,那么新创建的两个切片都共享这个底层的匿名数组
作为函数切片的参数
回到最开始的问题,当函数的参数是切片的时候,到底是传值还是传引用?从changeSlice函数中打出的参数s的地址,可以看出肯定不是传引用,毕竟引用都是一个地址才对。然而changeSlice函数内改变了s的值,也改变了原始变量slice的值,这个看起来像引用的现象,实际上正是我们前面讨论的切片共享底层数组的实现
即切片传递的时候,传的是数组的值,等效于从原始切片中再切了一次。原始切片slice和参数s切片的底层数组是一样的。因此修改函数内的切片,也就修改了数组
1 |
|
Slice或者array作为函数参数传递的时候,本质是传值而不是传引用。传值的过程复制一个新的切片,这个切片也指向原始变量的底层数组。(个人感觉称之为传切片可能比传值的表述更准确)。函数中无论是直接修改切片,还是append创建新的切片,都是基于共享切片底层数组的情况作为基础。也就是最外面的原始切片是否改变,取决于函数内的操作和切片本身容量
如果想要传引用的时候怎么办的,则传地址即可
1 |
|
总结
golang提供了array和slice两种序列结构。其中array是值类型。slice则是复合类型。slice是基于array实现的。slice的第一个内容为指向数组的指针,然后是其长度和容量。通过array的切片可以切出slice,也可以使用make创建slice,此时golang会生成一个匿名的数组
因为slice依赖其底层的array,修改slice本质是修改array,而array又是有大小限制,当超过slice的容量,即数组越界的时候,需要通过动态规划的方式创建一个新的数组块。把原有的数据复制到新数组,这个新的array则为slice新的底层依赖
数组还是切片,在函数中传递的不是引用,是另外一种值类型,即通过原始变量进行切片传入。函数内的操作即对切片的修改操作了。当然,如果为了修改原始变量,可以指定参数的类型为指针类型。传递的就是slice的内存地址。函数内的操作都是根据内存地址找到变量本身
则在最开始db层操作中,因为传入的肯定是空的model切片,在查找时必定会发生扩容,则此时就需要传入切片指针才行
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!