
在go语言中,数据类型可以分为值类型和指针类型。理解它们在map中的行为差异是至关重要的。
当我们将结构体作为Map的值类型时,选择map[int]vertex或map[int]*vertex会带来截然不同的行为。
当Map的值类型是结构体本身时(例如map[int]vertex),Map中存储的是结构体的副本。这意味着:
当Map的值类型是结构体指针时(例如map[int]*vertex),Map中存储的是结构体的内存地址。这意味着:
为了更清晰地展示这两种方式的区别,我们来看一个具体的Go语言程序:
立即学习“go语言免费学习笔记(深入)”;
package main
import "fmt"
type vertex struct {
x, y int
}
func main() {
// 声明两个Map:a 存储 vertex 结构体值,b 存储 *vertex 结构体指针
a := make(map[int]vertex)
b := make(map[int]*vertex)
// 创建一个 vertex 结构体实例,并获取其指针
v := &vertex{0, 0}
// 将 v 指向的结构体的值(副本)存入 a[0]
a[0] = *v
// 将 v 这个指针本身存入 b[0]
b[0] = v
// 第一次修改:通过原始指针 v 修改其指向的结构体
// 此时,v 和 b[0] 都指向同一个内存地址,a[0] 是 v 的一个独立副本
v.x, v.y = 4, 4
fmt.Printf("修改原始v后: a[0]: {%d, %d}, b[0]: {%d, %d}\n", a[0].x, a[0].y, b[0].x, b[0].y)
// 尝试直接修改 map[int]vertex 中的元素 - 编译错误
// a[0].x = 3 // 编译错误: cannot assign to (a[0]).x
// a[0].y = 3 // 编译错误: cannot assign to (a[0]).y
// 正确修改 map[int]vertex 中元素的方式:取出、修改、重新赋值
tempA := a[0] // 取出 a[0] 的副本
tempA.x = 3
tempA.y = 3
a[0] = tempA // 将修改后的副本重新赋值回 a[0]
fmt.Printf("通过取出-修改-赋值修改a[0]后: a[0]: {%d, %d}, b[0]: {%d, %d}\n", a[0].x, a[0].y, b[0].x, b[0].y)
// 修改 map[int]*vertex 中的元素 - 可以直接修改
// b[0] 是一个指针,直接修改其指向的结构体成员
b[0].x = 3
b[0].y = 3
fmt.Printf("直接修改b[0]指向的结构体后: a[0]: {%d, %d}, b[0]: {%d, %d}\n", a[0].x, a[0].y, b[0].x, b[0].y)
// 观察从Map中取出元素后的修改行为
u1 := a[0] // u1 是 a[0] 的一个值副本
u1.x = 2
u1.y = 2
// u1 的修改不影响 a[0],因为 u1 是 a[0] 的一个独立副本
fmt.Printf("u1 (a[0]的副本) 修改后: a[0]: {%d, %d}, b[0]: {%d, %d}\n", a[0].x, a[0].y, b[0].x, b[0].y)
u2 := b[0] // u2 是 b[0] 的一个指针副本,它们都指向同一个底层结构体
u2.x = 2
u2.y = 2
// u2 的修改通过指针影响了 b[0] 指向的底层结构体
fmt.Printf("u2 (b[0]的指针副本) 修改后: a[0]: {%d, %d}, b[0]: {%d, %d}\n", a[0].x, a[0].y, b[0].x, b[0].y)
}输出结果:
修改原始v后: a[0]: {0, 0}, b[0]: {4, 4}
通过取出-修改-赋值修改a[0]后: a[0]: {3, 3}, b[0]: {4, 4}
直接修改b[0]指向的结构体后: a[0]: {3, 3}, b[0]: {3, 3}
u1 (a[0]的副本) 修改后: a[0]: {3, 3}, b[0]: {3, 3}
u2 (b[0]的指针副本) 修改后: a[0]: {3, 3}, b[0]: {2, 2}解析:
v.x, v.y = 4, 4 后:
a[0].x = 3 编译错误的原因: 如前所述,Go语言的Map元素是不可寻址的。这意味着您不能获取Map中元素的内存地址,因此也无法直接对其进行成员修改。如果您尝试这样做,编译器会报错。正确的做法是先将元素取出(这会得到一个副本),修改副本,然后将修改后的副本重新赋值回Map。
b[0].x = 3 可以的原因:b[0] 存储的是一个指针。通过指针访问结构体成员是合法的,因为您实际上是在操作指针所指向的内存区域,而不是Map本身存储的那个指针值。
u1 := a[0] 与 u2 := b[0] 的区别:
理解了值类型和指针类型在Map中的行为后,我们可以根据实际需求做出选择:
var p *vertex
p = b[100] // 如果 b[100] 不存在,p 将是 nil
if p != nil {
fmt.Println(p.x)
} else {
fmt.Println("键不存在或值为nil")
}选择在Go语言Map中存储结构体的值类型还是指针类型,取决于您的具体应用场景、性能需求以及对数据修改行为的期望。
理解这两种方式的底层机制和行为差异,是编写高效、健壮Go程序的关键。
以上就是深入理解Go语言Map中结构体的存储:值类型与指针类型的选择与影响的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号