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的值类型,但对于值类型来说,每次访问确实都会生成一个新的副本。

浙公网安备 33010602011771号