从地图值附加切片不会影响地图

时间:2018-08-28 10:51:34

标签: go

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

切片的地址不会更改,因为在追加期间其上限足够大。那么为什么地图值不生效?

3 个答案:

答案 0 :(得分:4)

Go Slices: usage and internals中所述,两个片可能指向相同的存储位置,但可能具有不同的lencap属性。

答案 1 :(得分:3)

在Golang中,Go Slices: usage and internals的博客中提到了

  

切片不会复制切片的数据。它创建一个新的切片值   指向原始数组。这使得切片操作为   像处理数组索引一样有效。因此,修改   重新切片的元素(不是切片本身)修改了   原始切片:

slice = append(slice, 1)

因此,在上述情况下,它正在创建一个指向相同原始基础数组的新切片。这就是它显示相同地址的原因。

要获取切片指向的基础数组的数据,请使用reflectunsafe

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