关于递归和切片的问题

package main

import (
"fmt"
)

func main() {
var val []int = make([]int, 0, 100)
deepNum(3, val)
for _, v := range val {
fmt.Println(v)
}
}

func deepNum(deep int, ret []int) {
if deep > 0 {
deepNum(deep-1, ret)
} else {
fmt.Println("here")
ret = append(ret, 100)
}
}

输出:here


证明是有执行到here这边的,也是有对ret进入append操作,但是当deepNum返回时,val却是空,搞不懂,传入切片不应该呀。个人认为不是应该val里面已经有插入100这个值了吗?

已邀请:

hitzzc

赞同来自: foolbread looyun

这个问题在于slice作为形参时是值传递,而不是引用传递。slice的实际结构是reflect.SliceHeader,成员变量分别是len,cap,和一个指向数据存储地址的指针。你的长度为0,cap为100的ret作为参数传递时,实际上是复制了一个新的ret,这个新的ret和main函数内的ret拥有同样的len,cap和一个指向数据的指针。你做append的操作时,虽然cap够大,100 append到新的ret里,新的ret的len被修改成1,但是最外层的ret虽然数据里包含了100,但是len依然为0。而当range的时候是根据len去range的,所以没有输出。

stevewang

赞同来自: foolbread looyun

切片其实是一个结构:


type slice struct {
array unsafe.Pointer
len int
cap int
}

slice作为函数参数传递的时候实际上是复制了一个slice struct对象作为参数,因此deepNum函数里ret = append(ret, 100)并没有改变调用者环境里的ret
用切片指针可以解决这个问题,但是编程风格不是很好:


package main

import (
"fmt"
)

func main() {
var val []int = make([]int, 0, 100)
deepNum(3, &val)
for _, v := range val {
fmt.Println(v)
}
}

func deepNum(deep int, ret *[]int) {
if deep > 0 {
deepNum(deep-1, ret)
} else {
fmt.Println("here")
*ret = append(*ret, 100)
}
}

更符合go惯用法的写法是这样:


package main

import (
"fmt"
)

func main() {
var val []int = make([]int, 0, 100)
val = deepNum(3, val)
for _, v := range val {
fmt.Println(v)
}
}

func deepNum(deep int, ret []int) []int {
if deep > 0 {
return deepNum(deep-1, ret)
} else {
fmt.Println("here")
return append(ret, 100)
}
}

hitzzc

赞同来自: foolbread

你可以试着用unsafe.Pointer把ret强制转换成SliceHeader,然后修改len为1,再转换回[]int,这样你再range就能输出了

willee

赞同来自: foolbread

golang函数参数都是值传递,引用类型作为函数参数传递 ,只能改变值。要改变其他的,传指针吧!传指针也是值传递,传的是指针的copy

foolbread

赞同来自:

非常感谢两位的解答,学到了。O(∩_∩)O

要回复问题请先登录注册