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 的内存管理和一致性。具体原因包括:

  1. 值语义(Value Semantics):Go 倾向于使用值语义,而不是引用语义。当你访问 map 的值时,返回的是值的副本,而不是指向原始数据的引用。
  2. 内存安全性map 是动态数据结构,可能会在插入、删除等操作时重新分配内存(rehashing)。如果直接返回原始数据的引用,可能会导致外部代码持有的引用失效。因此,返回副本可以避免这种问题。
  3. 一致性: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 也反映了修改。
  • 这里的关键是:指针本身是副本,但它指向的内存地址未变。

结论

  • 值类型(如 studentm["lucy"] 每次返回一个独立的 student 结构体的副本,修改副本不会影响 map 中的原始数据。
  • 指针类型(如 *studentm["lucy"] 每次返回一个指针的副本,但指针指向的底层数据是同一个,修改会影响原始数据。
  • 因此,是否返回副本取决于 map 的值类型,但对于值类型来说,每次访问确实都会生成一个新的副本。
posted @   仁义礼智信的  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
历史上的今天:
2019-02-28 PHP $_SERVER详解
2019-02-28 php7 中?? 和 ?:的区别
点击右上角即可分享
微信分享提示