map中修改结构体值
为什么case5不能修改结构体里面的值,case6可以
func case5() {
type student struct {
name string
age int
}
m := map[string]student{
"lucy": {
name: "lucy",
age: 18,
},
}
//m["lucy"].age = 20
fmt.Println(m["lucy"].age)
}
func case6() {
type student struct {
name string
age int
}
m := map[string]*student{
"lucy": &student{
name: "lucy",
age: 18,
},
}
m["lucy"].age = 20
fmt.Println(m["lucy"].age)
}
是的,在 Go 中,当你通过 m["lucy"]
访问一个 map
的值时,每次访问都会返回该键对应值的副本,而不是直接引用原始数据。这是 Go 语言中 map
的设计特性之一。让我进一步详细说明:
为什么每次访问都返回副本?
Go 的 map
是一种键值对的集合,它的实现基于哈希表。在底层,map
存储的值是不可寻址的(not addressable),这是为了保证 map
的内存管理和一致性。具体原因包括:
- 值语义(Value Semantics):Go 倾向于使用值语义,而不是引用语义。当你访问
map
的值时,返回的是值的副本,而不是指向原始数据的引用。 - 内存安全性:
map
是动态数据结构,可能会在插入、删除等操作时重新分配内存(rehashing)。如果直接返回原始数据的引用,可能会导致外部代码持有的引用失效。因此,返回副本可以避免这种问题。 - 一致性:Go 的设计哲学是简单和可预测,返回副本的行为在所有情况下都是一致的。
因此,每次通过 m["lucy"]
访问时,Go 会创建一个新的 student
结构体的副本,并将其返回给你。
验证:每次访问都返回副本
我们可以写一个简单的代码来验证这一点:
package main
import "fmt"
type student struct {
name string
age int
}
func main() {
m := map[string]student{
"lucy": {
name: "lucy",
age: 18,
},
}
s1 := m["lucy"] // 获取第一个副本
s1.age = 20 // 修改副本
fmt.Println("s1:", s1.age) // 输出 20
fmt.Println("m['lucy']:", m["lucy"].age) // 输出 18,原始数据未变
s2 := m["lucy"] // 获取第二个副本
fmt.Println("s2:", s2.age) // 输出 18,和 s1 无关
}
输出:
s1: 20
m['lucy']: 18
s2: 18
分析:
s1 := m["lucy"]
获取了"lucy"
对应的student
的一个副本,修改s1.age
只影响这个副本,不影响map
中的原始数据。- 再次通过
m["lucy"]
获取s2
时,返回的是一个新的副本,和s1
无关,值依然是原始数据{name: "lucy", age: 18}
。 - 这证明每次访问
m["lucy"]
都会返回一个独立的副本。
对比指针的情况
如果 map
的值是指针(*student
),情况就不同了:
package main
import "fmt"
type student struct {
name string
age int
}
func main() {
m := map[string]*student{
"lucy": &student{
name: "lucy",
age: 18,
},
}
s1 := m["lucy"] // 获取指针的副本
s1.age = 20 // 通过指针修改原始数据
fmt.Println("s1:", s1.age) // 输出 20
fmt.Println("m['lucy']:", m["lucy"].age) // 输出 20,原始数据已被修改
}
输出:
s1: 20
m['lucy']: 20
分析:
s1 := m["lucy"]
获取的是指针(*student
)的副本,但这个副本仍然指向同一个student
实例。- 修改
s1.age
通过指针改变了原始数据,因此m["lucy"].age
也反映了修改。 - 这里的关键是:指针本身是副本,但它指向的内存地址未变。
结论
- 值类型(如
student
):m["lucy"]
每次返回一个独立的student
结构体的副本,修改副本不会影响map
中的原始数据。 - 指针类型(如
*student
):m["lucy"]
每次返回一个指针的副本,但指针指向的底层数据是同一个,修改会影响原始数据。 - 因此,是否返回副本取决于
map
的值类型,但对于值类型来说,每次访问确实都会生成一个新的副本。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
2019-02-28 PHP $_SERVER详解
2019-02-28 php7 中?? 和 ?:的区别