请教 golang slice 相关的问题

2019-07-09 11:29:51 +08:00
 wewin

我们知道 golang 中 slice 是引用类型,我声明长度为 10 的 slice,往其中插入了 6 个元素。打印能看到 addres4、addres5 地址相同,但是在 main 中打印 slice 的值和在 someChage 方法中打印的结果竟然不一样,请问大佬,这是什么原因?

代码:

package main

import "fmt"

func someChage(slice []int) {
	fmt.Printf("addres2: %p\n", slice)

	slice = append(slice, 1, 2, 3)
	fmt.Printf("addres3: %p\n", slice)

	slice = append(slice, 4, 5, 6)
	fmt.Printf("addres4: %p\n", slice)
	fmt.Println(slice)
}

func main() {
	slice := make([]int, 0, 20)
	fmt.Printf("addres1: %p\n", slice)
	someChage(slice)

	fmt.Printf("addres5: %p\n", slice)
	fmt.Println(slice)
}

结果:

addres1: 0xc00007a000
addres2: 0xc00007a000
addres3: 0xc00007a000
addres4: 0xc00007a000
[1 2 3 4 5 6]
addres5: 0xc00007a000
[]
4046 次点击
所在节点    Go 编程语言
25 条回复
wewin
2019-07-09 11:32:04 +08:00
期待大佬的回复
misaka19000
2019-07-09 11:45:05 +08:00
GGGG430
2019-07-09 11:47:47 +08:00
https://stackoverflow.com/questions/22811138/print-the-address-of-slice-in-golang

fmt.Printf("address of slice %p add of Arr %p \n", &slice, &intarr)
%p will print the address.
kidlj
2019-07-09 11:49:47 +08:00
Slice 并不是一个 pure 引用类型,更像是一个聚合类型:

type IntSlice struct {
ptr *int
len int
cap int
}

所以当你在 main 里将 slice 传给 someChange 函数,someChange 获得了一个 slice 的复制( Go 是参数传值),因此 someChange 里的 slice 和 main 函数里的 slice 不相关了。

两个函数里打印 slice 是相同的地址可能是因为打印的是 slice 底层 underlying array 的地址,两个 slice 指向的是同一个 underlying array,在 append 的过程中并没有改变(因为 cap 还够)。
petelin
2019-07-09 11:53:46 +08:00
Somechange 函数接受到的是 slice 对象的一个拷贝
Len cap 都是单独的 不过 底层数组确实是一个
在执行修改之后 main 的 slice len 还是 0 只不过底层数组多了 1-2-3-4 这个细节对于 slice 是隐藏的 所以看起来没有产生任何修改
fcten
2019-07-09 11:53:53 +08:00
slice 的地址相同并不意味着它们是同一个 slice。
你在 someChage 里操作的 slice 和 main 中的 slice 指向同一块数据,所以它们的地址相同。slice 本身是值传递的,所以在 someChage 里使用 append 操作 slice 并不会影响到 main 中的 slice。main 中的 slice 长度一直为 0,所以输出为 []。
sryanyuan
2019-07-09 11:53:54 +08:00
golang 都是值复制 没有引用
petelin
2019-07-09 11:58:19 +08:00
理解一下这段代码, 尤其是 s1[0] 会 panic.
func dos(s2 []int) {
s2 = append(s2, 1)
fmt.Printf("first element add: %p, slice addr: %p, real slice addr: %p", &s2[0], s2, &s2)
}
func TestSlice(t *testing.T) {
s1 := make([]int, 0, 20)
dos(s1)
fmt.Println(s1)
fmt.Println(s1[0])
}
tiedan
2019-07-09 12:01:59 +08:00
楼主,改成这样,你运行一下应该就明白了吧

package main

import "fmt"

func someChage(slice []int) {
slice[0] = 100
fmt.Println(slice)
fmt.Printf("addres2: %p\n", &slice)

slice = append(slice, 1, 2, 3)
fmt.Printf("addres3: %p\n", &slice)

slice = append(slice, 4, 5, 6)
fmt.Printf("addres4: %p\n", &slice)
fmt.Println(slice)
}

func main() {
slice := make([]int, 1, 10)
fmt.Printf("addres1: %p\n", &slice)
someChage(slice)

fmt.Printf("addres5: %p\n", &slice)
fmt.Println(slice)
}
tiedan
2019-07-09 12:03:37 +08:00
只是共用了一个底层数组,但是是不同的切片
tiedan
2019-07-09 12:05:31 +08:00
main 里面 slice 长度一直为 1 所以只能看到底层数组下标为 0 的那个元素
flyzero
2019-07-09 12:08:17 +08:00
有说的不对的请指出,函数参数都值传递,但是切片传的是指针地址值,
append 是在原有后面添加,所以没有改变指针值,你试试插入到第一位,就会变了
hduwillsky
2019-07-09 12:17:21 +08:00
关键在 append 返回
petelin
2019-07-09 12:17:34 +08:00
@flyzero 也不会变 看看上面的回复 你的理解有问题
mengzhuo
2019-07-09 12:26:06 +08:00
底层是数组,当你要 append 的数量大于数组大小 n 就会复制到 2n 的新数组中,这时 slice 里的指针发生变化。

好好看看基础就不会有这样的问题了
ruin2016
2019-07-09 12:45:56 +08:00
```
func someChage(slice []int) {
fmt.Printf("addres2: %p\n", &slice)

slice = append(slice, 1, 2, 3)
fmt.Printf("addres3: %p\n", &slice)

slice = append(slice, 4, 5, 6)
fmt.Printf("addres4: %p\n", &slice)
fmt.Println(slice)
}

func main() {
slice := make([]int, 0, 20)
fmt.Printf("addres1: %p\n", &slice)
someChage(slice)

fmt.Printf("addres5: %p\n", &slice)
fmt.Println(slice)
}

```

addres1: 0xc00007e040
addres2: 0xc00007e060
addres3: 0xc00007e060
addres4: 0xc00007e060
[1 2 3 4 5 6]
addres5: 0xc00007e040
[]

值引用!
wewin
2019-07-09 12:52:41 +08:00
@mengzhuo
你说的,len 超过 cap 的时候,会创建一个新的底层数组,数组的 cap 会按照 len 的两倍增加。是下面这种情况:

```
package main

import "fmt"

func someChage(slice []int) {
fmt.Printf("addres2: %p\n", slice)

slice = append(slice, 1, 2, 3)
fmt.Printf("addres3: %p\n", slice)

slice = append(slice, 4, 5, 6)
fmt.Printf("addres4: %p\n", slice)
fmt.Println(slice)
}

func main() {
slice := make([]int, 0, 4)
fmt.Printf("addres1: %p\n", slice)
someChage(slice)

fmt.Printf("addres5: %p\n", slice)
fmt.Println(slice)
}

```
输出结果:
```
addres1: 0xc00008a000
addres2: 0xc00008a000
addres3: 0xc00008a000
addres4: 0xc000090000
[1 2 3 4 5 6]
addres5: 0xc00008a000
[]
```

前前排大佬们的回复,那是些 get 到点的大佬们
impl
2019-07-09 12:53:26 +08:00
因为 main 里面的 slice 的 len 是还是 0,把最后一句改成 fmt.Println(slice[:6])试试
ScepterZ
2019-07-09 12:57:30 +08:00
slice 里存了地址 len cap 三个东西,你不穿指针进去的话,main 里的 slice 的 len 是没有改变的
可以输出 slice 的地址就明白了
shawn7
2019-07-09 14:10:33 +08:00
我还是不太明白,append 值不会改变,但是赋值可以

package main

import (
"fmt"
)

func addSome(s []int) {
s[0] = 1
}

func main() {
s := make([]int, 5)
fmt.Println(s)

addSome(s)
fmt.Println(s)
}

输出
[0 0 0 0 0]
[1 0 0 0 0]

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

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

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

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

© 2021 V2EX