mp := map[int][]int{}
slice := make([]int, 0, 1)
fmt.Printf("slice address:%p\n", slice)
mp[0] = slice
slice = append(slice, 1)
fmt.Println("after append")
fmt.Printf("slice address:%p\n", slice)
fmt.Println("slice:", slice)
fmt.Println("mp[0]:", mp[0])
fmt.Printf("mp[0] address:%p\n", mp[0])
输出:
slice address:0xc042008f78
after append
slice address:0xc042008f78
slice: [1]
mp[0]: []
mp[0] address:0xc042008f78
切片的地址不会更改,因为在追加期间其上限足够大。那么为什么地图值不生效?
答案 0 :(得分:4)
如Go Slices: usage and internals中所述,两个片可能指向相同的存储位置,但可能具有不同的len
和cap
属性。
答案 1 :(得分:3)
在Golang中,Go Slices: usage and internals的博客中提到了
切片不会复制切片的数据。它创建一个新的切片值 指向原始数组。这使得切片操作为 像处理数组索引一样有效。因此,修改 重新切片的元素(不是切片本身)修改了 原始切片:
slice = append(slice, 1)
因此,在上述情况下,它正在创建一个指向相同原始基础数组的新切片。这就是它显示相同地址的原因。
要获取切片指向的基础数组的数据,请使用reflect
和unsafe
:
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
data := *(*[1]int)(unsafe.Pointer(hdr.Data))
在Playground上工作的代码
答案 2 :(得分:2)
这是由于以下事实引起的:同一数据可以支持多个切片,但可以使用数据的不同“部分”。这意味着,是的,元素已添加到数据支持mp[0]
,但mp
中切片的长度未更改。您可以手动执行以下操作:
fmt.Println(mp[0][:1])
会打印[1]
。
您可以使用slice[:cap(slice)]
将任何片增加到其容量,而无需更改基础数据。如果slice[:n]
,cap(slice) < n
会惊慌。
另一方面,slice[n]
会在len(slice) <= n
时惊慌。我认为前者有可能允许切片的增长而无需更改基础数据(在可能的范围内)。我会说后者是“正常”行为。
这也解释了为什么mp[0][:2]
紧急,因为cap(mp[0])
是1
。
有关更多详细信息,您可能需要阅读Flimzy建议的this official blog post。