go slice 的一个疑问

2020-03-04 11:46:23 +08:00
 yujianwjj
package main

import (
	"fmt"
)

func test(a []int) {
	a = append(a, 4)
}

func main() {
	s := make([]int, 3, 4)
	s[0] = 1
	s[1] = 2
	s[2] = 3
	fmt.Println(s)//1, 2, 3
	test(s)
	fmt.Println(s)//1, 2, 3
}

我认为第二个 println 打印出来的应该是 1, 2, 3, 4, 但是实际打印的是 1, 2, 3

2810 次点击
所在节点    Go 编程语言
21 条回复
zoowii
2020-03-04 11:48:34 +08:00
a=append(a, 4)执行前后的 a 不是同一个 slice, 参数 a 传的是引用的值,不改变原来的 s 变量
yujianwjj
2020-03-04 11:50:30 +08:00
@zoowii cap(a) 是 4,实际 len(a) 是 3,为啥 append 前后的 a 不是同一个 slice ?
kuro1
2020-03-04 11:51:31 +08:00
老一套 指针问题
auta
2020-03-04 11:55:57 +08:00
@yujianwjj #2 外面的 s 的 len 没有变成 4
lhx2008
2020-03-04 11:56:33 +08:00
s 这个 struct 没有被改,里面存了 len 的数据,a 是另外一个 struct
AzadCypress
2020-03-04 12:02:46 +08:00
type slice struct{
array unsafe.Pointer
len int
cap int
}
当你用 slice 作为参数时,传进去的是这个结构体的 copy
所以改变 slice 中的值是有效的,涉及到长度 /容量的在副本上进行 是无效的
yujianwjj
2020-03-04 12:03:55 +08:00
@auta 懂了
cmdOptionKana
2020-03-04 12:06:18 +08:00
你把 test 函数改成这样

func test(a *[]int) { *a = append(*a, 4) }

然后这样调用 test(&s)

就可以了。
ifconfig
2020-03-04 14:11:19 +08:00
这是值传递,很多语言都是值传递吧
zhs227
2020-03-04 14:22:04 +08:00
说到点子上的人不多。除了 auta,外面的 s.len 没变,但是 s[4]的内容实际是有了,只是 length 仍然是 3.
具体可以看看 playground: https://play.golang.org/p/JjP8z4xQ1Qa
luoqeng
2020-03-04 14:39:11 +08:00
底层用的同一块内存,但是 s 与 a 的里记录的长度不一样。
luoqeng
2020-03-04 15:08:03 +08:00
sSliceArrayPointer := unsafe.Pointer(*(*unsafe.Pointer)(unsafe.Pointer(&s)))

offset3Pointer := unsafe.Pointer(uintptr(sSliceArrayPointer ) + unsafe.Sizeof(&s[0])*3)

fmt.Println("offset s[3]:", *(*int)(offset3Pointer))

输出 offset s[3]: 4
CEBBCAT
2020-03-04 18:38:34 +08:00
visitant
2020-03-04 18:50:35 +08:00
slice 实际上是一个结构体 struct{ptr,len,cap},ptr 指向存储数据的数组,len/cap 表示当前长度和容量,你传到函数内的是一个 struct 的拷贝,在一个内部函数 append 不会影响到外部函数的 len.
index90
2020-03-04 18:53:16 +08:00
你把 slice 类型看作是一个大概这样子的 struct
type Slice struct {
len int
mem *unsafe.Pointer
... other info
}
函数参数是值传递,就是把 s 复制了一遍到 a
其实 a 和 s 只是用了同一个内存指针,其他都是独立的
v2exe2v
2020-03-04 18:54:51 +08:00
func test(a []int) 这里的 a 是 s 的一个拷贝,所以对 a 的修改不影响外面的 s。我猜传 *[]int 可以
CEBBCAT
2020-03-04 20:07:37 +08:00
@v2exe2v 影响的,你可以看我上面贴的 gist,也可以看看前几楼的解释
kuro1
2020-03-05 10:59:12 +08:00
```
package main

import (
"fmt"
)

func test(a []int) []int{
a = append(a, 4)
return a
}

func main() {
s := make([]int, 3, 4)
s[0] = 1
s[1] = 2
s[2] = 3
fmt.Println(s) //[1 2 3]
a := test(s)
fmt.Println(a) //[1 2 3 4]
s = append(s,5)
fmt.Println(s) //[1 2 3 4]
fmt.Println(a) //[1 2 3 5]
}
```
kuro1
2020-03-05 11:03:04 +08:00
更正,最后两行
fmt.Println(s) //[1 2 3 5]
fmt.Println(a) //[1 2 3 5]
newmiao
2020-03-07 17:30:51 +08:00
建议了解下 slice 底层结构,再加上 Go 里传参都是值传递就好理解了
https://www.v2ex.com/t/650724

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/649712

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX