go 新手求助,关于 slice 的问题。

2022-02-14 10:44:50 +08:00
 tousfun
func main() {
	s2 := make([]int, 0, 10)
	fmt.Printf("%p\n", s2)
	addValue(s2)
	fmt.Println(s2)
}

func addValue(list []int) {
	fmt.Printf("%p\n", list)
	list = append(list, 1, 2, 3, 4)
	fmt.Println(list)
	fmt.Printf("%p\n", list)
}

最后打印出的结果是:

0x140000b4000
0x140000b4000
[1 2 3 4]
0x140000b4000
[]

为什么 s2 和 list 指向的地址相同,保存的值不同呢

2246 次点击
所在节点    Go 编程语言
15 条回复
mainjzb
2022-02-14 10:50:04 +08:00
func addValue(list []int) []int{
fmt.Printf("%p\n", list)
list = append(list, 1, 2, 3, 4)
fmt.Println(list)
fmt.Printf("%p\n", list)
return list
}
hujun528
2022-02-14 10:58:33 +08:00
nulIptr
2022-02-14 11:00:48 +08:00
因为 slice 除了起始地址之外还保存了长度,你 main 函数里面的 s2 长度还是 0 ,长度还是 0 的原因的参数是值传递,addValue 里面修改的是拷贝一次的 slice 结构,而不是 main 函数里的 s2
shyz
2022-02-14 11:12:07 +08:00
函数都是值拷贝,sli 是引用类型,list 拷贝了 sli 的地址,直接输出地址肯定一样了。
monetto
2022-02-14 11:18:35 +08:00
可以这样理解,一个 slice 实际是一个 []int + 长度。
你在函数里 addValue 之后,数组的下一位其实是赋值成功的。(如果没有触发 Slice 重新分配数组内存)
但是,长度是 “形参” ,函数内的改变不会影响函数外部。

也就是说,其实整个过程,是赋值成功的,但是,由于长度没有发生变化,addValue 外部的逻辑无法感知到新添加的元素。

想要验证的话,可以通过 unsafe 获取 addValue 添加元素的地方的那块内存,会发现实际是添加成功了的。

顺便一提,如果 addValue 的过程中,数组的长度达到阈值了,那么就会触发 Slice 重新分配内存,这个时候,传入函数中的 那个时刻的那个 数组 ,实际上是没有任何变化的。
tousfun
2022-02-14 11:23:29 +08:00
@mainjzb
@hujun528
@nulIptr
@shyz 谢谢各位,明白了
tousfun
2022-02-14 11:29:22 +08:00
@monetto 非常感谢,明白了
hujun528
2022-02-14 11:37:49 +08:00
package main

import (
"fmt"
"testing"
)

func Test_test33(T *testing.T) {
test33()
}

func test33() {
s2 := make([]int, 2, 10) //初始长度 2
s2 = append(s2, 66, 88) //长度+2
fmt.Println(s2) //s2 总长度 =4
fmt.Printf("%p\n", s2)
addValue(s2) //传递 切片底向的 底层数组 针针
fmt.Println(s2) //s2 总长度 4 并没有改变

s2 = s2[:8] //如果改变切片 s2 的长度 =list 长度,
fmt.Println(s2) // 是不是和 list 输出一样了
}

func addValue(list []int) {
fmt.Printf("%p\n", list) //此切片 list 并不完全等于 切片 s2,切片之间也是无法进行比较的,list 只是和 s2 共用同一底层数组 ,切片 还有长度和容量
list = append(list, 1, 2, 3, 4) //list 长度 +4 ,此操作 并不会改变 s2 的切片长度
fmt.Printf("%p\n", list)
fmt.Println(list)

}
iyear
2022-02-14 11:39:27 +08:00
自己写过一篇关于 slice 参数传递的文章,可以看看

https://blog.ljyngup.com/archives/868.html/
wheeler
2022-02-14 12:22:03 +08:00
go slice 的 api 太不直观了。好像是肯汤普森写的。
WhereverYouGo
2022-02-14 13:02:24 +08:00
@iyear #9 博客不错
CEBBCAT
2022-02-14 13:12:56 +08:00
Go 博客可以多读一读 https://go.dev/blog/slices-intro
ryalu
2022-02-14 13:43:50 +08:00
lpxxn
2022-02-14 16:31:20 +08:00
Lexgni
2022-02-14 17:47:47 +08:00
你的 len 是 0 ,然后你追加的话原来的放不下,就会重新分配一个,你打印一下追加后的 list 就会发现和前面不一样了

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

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

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

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

© 2021 V2EX