我们知道 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
[]
1
wewin OP 期待大佬的回复
|
2
misaka19000 2019-07-09 11:45:05 +08:00 via Android 1
|
3
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. |
4
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 还够)。 |
5
petelin 2019-07-09 11:53:46 +08:00 via iPhone
Somechange 函数接受到的是 slice 对象的一个拷贝
Len cap 都是单独的 不过 底层数组确实是一个 在执行修改之后 main 的 slice len 还是 0 只不过底层数组多了 1-2-3-4 这个细节对于 slice 是隐藏的 所以看起来没有产生任何修改 |
6
fcten 2019-07-09 11:53:53 +08:00
slice 的地址相同并不意味着它们是同一个 slice。
你在 someChage 里操作的 slice 和 main 中的 slice 指向同一块数据,所以它们的地址相同。slice 本身是值传递的,所以在 someChage 里使用 append 操作 slice 并不会影响到 main 中的 slice。main 中的 slice 长度一直为 0,所以输出为 []。 |
7
sryanyuan 2019-07-09 11:53:54 +08:00
golang 都是值复制 没有引用
|
8
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]) } |
9
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) } |
10
tiedan 2019-07-09 12:03:37 +08:00
只是共用了一个底层数组,但是是不同的切片
|
11
tiedan 2019-07-09 12:05:31 +08:00
main 里面 slice 长度一直为 1 所以只能看到底层数组下标为 0 的那个元素
|
12
flyzero 2019-07-09 12:08:17 +08:00 via Android
有说的不对的请指出,函数参数都值传递,但是切片传的是指针地址值,
append 是在原有后面添加,所以没有改变指针值,你试试插入到第一位,就会变了 |
13
hduwillsky 2019-07-09 12:17:21 +08:00 via iPhone
关键在 append 返回
|
15
mengzhuo 2019-07-09 12:26:06 +08:00 via iPhone
底层是数组,当你要 append 的数量大于数组大小 n 就会复制到 2n 的新数组中,这时 slice 里的指针发生变化。
好好看看基础就不会有这样的问题了 |
16
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 [] 值引用! |
17
wewin OP @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 到点的大佬们 |
18
impl 2019-07-09 12:53:26 +08:00 via Android
因为 main 里面的 slice 的 len 是还是 0,把最后一句改成 fmt.Println(slice[:6])试试
|
19
ScepterZ 2019-07-09 12:57:30 +08:00
slice 里存了地址 len cap 三个东西,你不穿指针进去的话,main 里的 slice 的 len 是没有改变的
可以输出 slice 的地址就明白了 |
20
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] |
21
reage 2019-07-09 14:19:34 +08:00
slice := make([]int, 0, 20)
你将 20 改为 2 看下,就看到变化。了解下 map 的 cap 参数 |
22
rrfeng 2019-07-09 14:41:35 +08:00
|
23
shawn7 2019-07-09 14:49:34 +08:00 via Android
明白了,2 楼发的文章讲的很清楚。谢谢楼主
|
24
liulaomo 2019-07-09 20:34:34 +08:00
|
25
jimmzhou 2019-07-09 22:43:28 +08:00
当把 slice 作为参数,本身传递的是值,但其内容就 byte* array,实际传递的是引用,所以可以在函数内部修改,但如果对 slice 本身做 append,而且导致 slice 进行了扩容,实际扩容的是函数内复制的一份切片,对于函数外面的切片没有变化。
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: 0xc00009c000 addres2: 0xc00005e420 addres3: 0xc00005e420 addres4: 0xc00005e420 [1 2 3 4 5 6] addres5: 0xc00009c000 [1 2 3 4 5 6] |