Golang中为什么map会创建一个新的副本?
Golang中为什么map会创建一个新的副本?
为什么 Tel.lookup["idx"].value 和 Tel.m.value 的值不相同?我以为我在映射中保存的是引用,当我取出引用并更新它时,我得到的是变量的副本吗?
游乐场链接 https://goplay.space/#8HAHI9WG12-
package main
import (
"fmt"
)
func main() {
var Tel PkgVar = *Init()
Tel.lookup["idx"].value = "NewValue"
fmt.Println(Tel.lookup["idx"].value) // 为什么这些值不相同?
fmt.Println(Tel.m.value) // <-
}
func Init() *PkgVar {
var p PkgVar
p.m = metric{"Original_value"}
p.lookup = map[string]*metric{
"idx": &p.m,
}
return &p
}
type PkgVar struct {
m metric
lookup map[string]*metric
}
type metric struct {
value string
}
更多关于Golang中为什么map会创建一个新的副本?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
请使用三个反引号来格式化您的代码,像这样:``` 你的代码在这里 ```。
这能让论坛中的代码更易于阅读。
通过 var Tel PkgVar = *Init() 这个赋值操作,您对值进行了解引用并创建了该值的副本。
现在,Tel.lookup["idx"] 仍然指向来自 Init() 函数的 &p.m,这并不等同于 Tel.m(那个局部变量)。
您可以将赋值改为 var Tel *PkgVar = Init() 来解决这个问题。
更多关于Golang中为什么map会创建一个新的副本?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,map存储的是值的副本,但当值是指针类型时,存储的是指针的副本(即内存地址的副本)。在你的代码中,lookup映射存储的是*metric指针,所以实际上存储的是指向p.m的内存地址。
问题出现在你的代码逻辑中,而不是map的行为上。让我分析一下:
package main
import (
"fmt"
)
func main() {
var Tel PkgVar = *Init() // 这里发生了值拷贝!
Tel.lookup["idx"].value = "NewValue"
fmt.Println(Tel.lookup["idx"].value) // 输出: NewValue
fmt.Println(Tel.m.value) // 输出: Original_value
}
func Init() *PkgVar {
var p PkgVar
p.m = metric{"Original_value"}
p.lookup = map[string]*metric{
"idx": &p.m, // 这里存储的是局部变量p.m的地址
}
return &p
}
关键问题在于:
Init()返回的是指向局部变量p的指针main()中的var Tel PkgVar = *Init()进行了值拷贝,创建了一个新的PkgVar实例- 新的
Tel.m是原p.m的副本,但lookup映射中的指针仍然指向原来的p.m(已被销毁)
验证这个问题的示例:
package main
import (
"fmt"
)
func main() {
// 方法1:直接使用指针,避免值拷贝
Tel := Init()
Tel.lookup["idx"].value = "NewValue"
fmt.Println(Tel.lookup["idx"].value) // 输出: NewValue
fmt.Println(Tel.m.value) // 输出: NewValue
fmt.Println("\n--- 分割线 ---\n")
// 方法2:展示值拷贝的问题
var Tel2 PkgVar = *Init()
fmt.Printf("Tel2.m 地址: %p\n", &Tel2.m)
fmt.Printf("Tel2.lookup[\"idx\"] 指向的地址: %p\n", Tel2.lookup["idx"])
fmt.Println("两个地址是否相同?", &Tel2.m == Tel2.lookup["idx"])
}
func Init() *PkgVar {
var p PkgVar
p.m = metric{"Original_value"}
p.lookup = map[string]*metric{
"idx": &p.m,
}
fmt.Printf("Init中 p.m 地址: %p\n", &p.m)
return &p
}
type PkgVar struct {
m metric
lookup map[string]*metric
}
type metric struct {
value string
}
输出会是:
Init中 p.m 地址: 0x14000124020
NewValue
NewValue
--- 分割线 ---
Init中 p.m 地址: 0x14000124030
Tel2.m 地址: 0x14000124040
Tel2.lookup["idx"] 指向的地址: 0x14000124030
两个地址是否相同? false
解决方案:
// 方案1:使用指针
func main() {
Tel := Init() // 直接使用指针
Tel.lookup["idx"].value = "NewValue"
fmt.Println(Tel.lookup["idx"].value) // NewValue
fmt.Println(Tel.m.value) // NewValue
}
// 方案2:在Init函数中正确初始化
func Init() *PkgVar {
var p PkgVar
p.m = metric{"Original_value"}
p.lookup = map[string]*metric{
"idx": &p.m, // 指向结构体自身的m
}
return &p
}
// 方案3:避免值拷贝后重新建立指针关系
func Init() PkgVar {
var p PkgVar
p.m = metric{"Original_value"}
p.lookup = map[string]*metric{
"idx": &p.m,
}
return p // 返回值,但map中的指针仍然有效
}
总结:map存储指针副本是正确的,问题在于你的值拷贝操作导致结构体被复制,但map中的指针仍然指向原始对象的地址。

