2018年4月19日星期四

golang slice函数值传递的“隐患”

    直接上现场,看看你能不能一眼识破“隐患”根因!

    下文函数基本功能是针对一个分片silce的数据,设置将前toCount个数据更新为100以内随机数,如果toCount大于当前的slice总长度,则扩大到toCount大小并填满100以内的随机数。
    代码如下:
package main
import ( "fmt" "math/rand")
func getRand() int {
return rand.Int() % 100
}
func fillInRandom1(ori []int, toCount int) {
fillInRandom2(&ori, toCount)}
func fillInRandom2(ori *[]int, toCount int) {
if toCount <= len(*ori) {
for i:=0; i< toCount; i++ {
(*ori)[i] = getRand() }
} else {
for i:=0; i< len(*ori); i++ {
(*ori)[i] = getRand() }
for i:=0; i< toCount - len(*ori); i++ {
*ori = append(*ori, getRand()) }
return }
}
func printSlice(s []int) {
for _, i := range s {
fmt.Printf("%v ", i) }
fmt.Println()}
func main() {
fmt.Println("slice参数值传递效果:") nums := make([]int, 4, 5) fillInRandom1(nums, 2) printSlice(nums) fillInRandom1(nums, 4) printSlice(nums) fillInRandom1(nums, 8) printSlice(nums)
fmt.Println("slice指针传递效果:") nums = make([]int, 4, 5) fillInRandom2(&nums, 2) printSlice(nums) fillInRandom2(&nums, 4) printSlice(nums) fillInRandom2(&nums, 8) printSlice(nums)
}
输出如下:

slice参数值传递效果:
10 51 0 0
21 51 37 20
58 48 16 49
slice指针传递效果:
74 36 0 0
15 73 68 91
90 31 73 56 11 37
    我们可以看到两种情况下,明显是第二种指针传递的方式能够满足要求。第一种方式很奇怪,数字发生了改变,说明slice不是值拷贝传入函数的,但是扩长度却没有生效。什么原因呢?
    这个还是需要从slice的内部原理讲起了。
slice底层是利用数组array进程存储,slice变量本身对应内部结构包括指向
  • 数组的指针address pointer
  • 可用数组总容量Capacity
  • slice已存储的数据量Length
而参数传递的时候,拷贝的就是slice变量,而不包含整个数组。如下图所示:


这样返回main函数后,能看到的就是修改了一部分的原数组。append动作新建的数组只能在fillInRandom1函数内部可见,函数退出即销毁。

而使用指针的方式,则所有的变更都操作的原来的底层数组,append也就能看到了,指针的基本逻辑。

所以,如果在函数内部涉及到slice长度的变化,切记需要采用指针传递的方式!

没有评论:

发表评论