Golang Go语言中请教 slice 相关的问题
Golang Go语言中请教 slice 相关的问题
我们知道 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
[]
更多关于Golang Go语言中请教 slice 相关的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
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.
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 还够)。
Somechange 函数接受到的是 slice 对象的一个拷贝
Len cap 都是单独的 不过 底层数组确实是一个
在执行修改之后 main 的 slice len 还是 0 只不过底层数组多了 1-2-3-4 这个细节对于 slice 是隐藏的 所以看起来没有产生任何修改
slice 的地址相同并不意味着它们是同一个 slice。
你在 someChage 里操作的 slice 和 main 中的 slice 指向同一块数据,所以它们的地址相同。slice 本身是值传递的,所以在 someChage 里使用 append 操作 slice 并不会影响到 main 中的 slice。main 中的 slice 长度一直为 0,所以输出为 []。
golang 都是值复制 没有引用
理解一下这段代码, 尤其是 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])
}
楼主,改成这样,你运行一下应该就明白了吧
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)
}
只是共用了一个底层数组,但是是不同的切片
main 里面 slice 长度一直为 1 所以只能看到底层数组下标为 0 的那个元素
有说的不对的请指出,函数参数都值传递,但是切片传的是指针地址值,
append 是在原有后面添加,所以没有改变指针值,你试试插入到第一位,就会变了
关键在 append 返回
也不会变 看看上面的回复 你的理解有问题
底层是数组,当你要 append 的数量大于数组大小 n 就会复制到 2n 的新数组中,这时 slice 里的指针发生变化。
好好看看基础就不会有这样的问题了
<br>func someChage(slice []int) {<br> fmt.Printf("addres2: %p\n", &slice)<br><br> slice = append(slice, 1, 2, 3)<br> fmt.Printf("addres3: %p\n", &slice)<br><br> slice = append(slice, 4, 5, 6)<br> fmt.Printf("addres4: %p\n", &slice)<br> fmt.Println(slice)<br>}<br><br>func main() {<br> slice := make([]int, 0, 20)<br> fmt.Printf("addres1: %p\n", &slice)<br> someChage(slice)<br><br> fmt.Printf("addres5: %p\n", &slice)<br> fmt.Println(slice)<br>}<br><br>
addres1: 0xc00007e040
addres2: 0xc00007e060
addres3: 0xc00007e060
addres4: 0xc00007e060
[1 2 3 4 5 6]
addres5: 0xc00007e040
[]
值引用!
你说的,len 超过 cap 的时候,会创建一个新的底层数组,数组的 cap 会按照 len 的两倍增加。是下面这种情况:<br>package main<br><br>import "fmt"<br><br>func someChage(slice []int) {<br> fmt.Printf("addres2: %p\n", slice)<br><br> slice = append(slice, 1, 2, 3)<br> fmt.Printf("addres3: %p\n", slice)<br><br> slice = append(slice, 4, 5, 6)<br> fmt.Printf("addres4: %p\n", slice)<br> fmt.Println(slice)<br>}<br><br>func main() {<br> slice := make([]int, 0, 4)<br> fmt.Printf("addres1: %p\n", slice)<br> someChage(slice)<br><br> fmt.Printf("addres5: %p\n", slice)<br> fmt.Println(slice)<br>}<br><br>
输出结果:<br>addres1: 0xc00008a000<br>addres2: 0xc00008a000<br>addres3: 0xc00008a000<br>addres4: 0xc000090000<br>[1 2 3 4 5 6]<br>addres5: 0xc00008a000<br>[]<br>
前前排大佬们的回复,那是些 get 到点的大佬们
因为 main 里面的 slice 的 len 是还是 0,把最后一句改成 fmt.Println(slice[:6])试试
slice 里存了地址 len cap 三个东西,你不穿指针进去的话,main 里的 slice 的 len 是没有改变的
可以输出 slice 的地址就明白了
slice := make([]int, 0, 20)
你将 20 改为 2 看下,就看到变化。了解下 map 的 cap 参数
刚好前几天看到一篇文章,可以解惑。
《 Go Slices are Fat Pointers 》
https://nullprogram.com/blog/2019/06/30/
明白了,2 楼发的文章讲的很清楚。谢谢楼主
当把 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]
在Golang中,slice(切片)是一个非常强大且灵活的数据结构,它基于数组构建,但提供了动态长度和容量。以下是对slice的一些常见问题的解答:
-
slice的底层实现:slice在底层是一个包含三个元素的结构体:指向数组的指针(Pointer)、切片的长度(Length)和切片的容量(Capacity)。
-
slice的创建:可以使用
make
函数或者通过数组来创建slice。例如,s := make([]int, 5)
会创建一个长度为5、容量为5的整数slice。 -
slice的扩容:当向slice添加元素且超过其容量时,Go会自动分配一个更大的底层数组并复制原有元素。扩容策略并不是简单的倍增,而是依赖于当前容量。
-
slice的共享与拷贝:slice是引用类型,因此不同的slice可以共享同一个底层数组。使用
copy
函数可以创建slice的副本,避免共享带来的问题。 -
slice的遍历:可以使用
for
循环或range
关键字来遍历slice中的元素。range
还提供了元素的索引和值。 -
slice的切片操作:通过
s[low:high]
语法,可以创建一个新的slice,该slice包含从low
到high-1
的元素。如果省略low
,则默认为0;如果省略high
,则默认为slice的长度。
理解slice的这些特性,对于编写高效、安全的Go代码至关重要。希望这些解答能帮到你,如果有更具体的问题,欢迎继续提问!