package main
import (
"fmt"
"log"
)
func main() {
arr := make([]int, 3, 3)
fmt.Println(arr, len(arr), cap(arr))
log.Printf("%p", arr)
addElems(arr)
fmt.Println(arr, len(arr), cap(arr))
log.Printf("%p", arr)
}
func addElems(arr []int) {
log.Printf("%p", arr)
arr = append(arr, 4)
arr = append(arr, 5)
arr = append(arr, 6)
fmt.Println(arr, len(arr), cap(arr))
log.Printf("%p", arr)
}
切片在函数内部发生变化,这就是 Go 的工作方式,但如果你执行追加操作,那么一切都会不同。我认为这很可能是因为append返回新地址并将其插入到arr变量中,但不清楚为什么arr本身没有变异。
我决定再举一个例子
package main
import (
"fmt"
"log"
)
func main() {
arr := make([]int, 3, 8)
fmt.Println(arr, len(arr), cap(arr))
log.Printf("%p", arr)
addElems(arr)
fmt.Println(arr, len(arr), cap(arr))
log.Printf("%p", arr)
}
func addElems(arr []int) {
log.Printf("%p", arr)
arr = append(arr, 4)
arr = append(arr, 5)
arr = append(arr, 6)
fmt.Println(arr, len(arr), cap(arr))
log.Printf("%p", arr)
}
这里append没有返回新的地址,为什么函数出口处有一个arr,但在main函数中运行后它已经不同了?
切片不是指针。这是一个包含三个字段的数据结构,通过 value传递给函数。
在这里,看:
该
main
变量arr
位于该地址0xc000010018
,指向数据的指针是0xc00001a018
但在函数中,
addElems
所有操作都发生在完全不同的对象上,该对象位于内存中的地址处0xc000010060
。那。该对象中的指针已更改不会以任何方式反映在第一个对象上。与len
and类似cap
,它们更改了堆栈上的值addElems
,但这些更改不会以任何方式影响原始对象。在这种情况下,
make([]int, 3, 8)
指针没有改变,因为不需要重新分配。但即使在这种情况下,原始切片也不知道副本中的变化,因此尽管添加了三个元素,但也没有发生变化len
。cap
如果需要更新原始对象,则需要传递一个指针
或返回修改后的对象
样式指南推荐第二种方法,返回更新后的值。
切片下的数组本身发生了变化,这可以通过写入after
arr
看到,这将基于同一个数组创建一个长度等于数组容量的新切片。arr = arr[:cap(arr)]
main
addElems(arr)
slice
arr
fromaddElems
是从 from 的切片创建的切片main
。您可以在此处阅读有关数组和切片的更多信息: https: //go.dev/blog/slices-intro
您可以像这样缩短日志:
fmt.Printf(“%p %v len=%v cap=%v\n”, arr, arr, len(arr), cap(arr))
追加适用于副本,而不适用于底层数组
如果需要追加返回新地址,请添加 3 个以上的值。
append 使用 cap= cap(old slice)*2 创建一个新切片,虽然旧切片的 cap 对他来说已经足够了,但他却陷入了阑尾。
事实证明,为了更改地址,您需要添加的不仅仅是 cap(arr)
我的输出是这样的:
每次扩展上限时(即每次复制到新的基本数组时),地址都会发生变化