Golang中无法对map的指针进行索引怎么办

Golang中无法对map的指针进行索引怎么办 作为一个Go语言新手,我注意到一个现象,希望能得到解释:

假设我有一个映射(map),通过引用传递给函数:

myMap := make(map[string]int)

myFunc(&myMap)

在函数内部,我必须先将该映射复制到函数内的局部变量,修改该局部变量,然后在函数返回前重新复制回去,这样我实际想要修改的映射才会被修改:

func myFunc(tMap *map[string]int) {
	tmpMap := *tMap

	tmpMap["Goose"] = 11
	tmpMap["Gander"] = 11

	*tMap = tmpMap
}

如果我尝试直接修改传入的映射,这是行不通的。例如下面的代码:

func myBrokeFunc(tMap *map[string]int) {
	tMap["Goose"] = 14
	*tMap["Gander"] = 14

}

会导致我的IDE在两个实例中都报错,提示无法对指向映射的指针进行索引。

这是什么原因造成的?我理解可能存在并发写入的问题,但对于其他变量,我可以这样做。这对我来说是一个非常有趣的问题,我很想知道其背后的原理。


更多关于Golang中无法对map的指针进行索引怎么办的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

感谢大家的精彩回答!这真的让我受益匪浅。

更多关于Golang中无法对map的指针进行索引怎么办的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


没有理由传递一个指向 map 的指针。直接传递 map 即可,它不会复制内容。

map 是引用类型,函数传递 map 会改变原始数据,无需使用 *map 类型

下面的文章应该能解答你关于映射(map)工作原理的疑问。

https://dave.cheney.net/2017/04/30/if-a-map-isnt-a-reference-variable-what-is-it

另一方面,对映射使用指针并不是一个好主意,因为:

在 Go 的所有内置数据结构中,映射是唯一会在内部移动数据的结构。当你插入或删除条目时,映射可能需要重新平衡自身以保持其 O(1) 的保证。这就是为什么映射值不可寻址的原因。

https://dave.cheney.net/2015/12/07/are-go-maps-sensitive-to-data-races

+1 @Yamil_Bracho

映射是一种特殊的数据类型,它们既不是结构体,也不是指向结构体的指针(指向 runtime.hmap 对象的指针),但与之类似。

每次使用映射时,在编译时,与映射相关的代码将被自动生成的代码替换。这限制了您操作映射的方式(以及您应如何看待它们)。

@Yamil_Bracho 给出了正确答案,使用 (*tMap)[“Gander”] = 14

确实如此。 然而,请允许我解释一下出现“不支持索引”这一消息的原因。

当你写下:

*tMap["Gander"] = 14

编译器会将这个表达式视为:

*(tMap["Gander"]) = 14

于是便出现了 tMap 不支持索引的错误信息,因为它是一个指针。

只需写成类似这样的形式:

(*tMap)["Gander"] = 14

这样,Go 会先计算括号内的表达式并返回被指向的对象,该对象是一个映射,之后你就可以像往常一样使用它了。

在Go语言中,map本身就是一个引用类型(底层是指向runtime.hmap的指针),因此直接传递map即可在函数内部修改原始数据,无需传递指针。你遇到的问题是由于Go语言语法限制导致的。

核心原因

  1. 语法限制:Go语言设计上不允许直接对map指针进行索引操作,必须解引用后才能使用索引语法。
  2. map的引用语义map变量本身已经是引用,传递时复制的是这个引用(类似指针),而不是底层数据。

解决方案示例

方案1:直接传递map(推荐)

func myFunc(m map[string]int) {
    m["Goose"] = 11
    m["Gander"] = 11
    // 无需返回或重新赋值
}

func main() {
    myMap := make(map[string]int)
    myFunc(myMap) // 直接传递map
    fmt.Println(myMap) // 输出: map[Goose:11 Gander:11]
}

方案2:必须使用指针时的正确写法

func myFunc(tMap *map[string]int) {
    (*tMap)["Goose"] = 11  // 显式解引用
    (*tMap)["Gander"] = 11
}

func main() {
    myMap := make(map[string]int)
    myFunc(&myMap)
    fmt.Println(myMap) // 输出: map[Goose:11 Gander:11]
}

方案3:使用临时变量简化指针操作

func myFunc(tMap *map[string]int) {
    m := *tMap  // 一次解引用
    m["Goose"] = 11
    m["Gander"] = 11
    // 无需重新赋值,因为m和*tMap引用同一底层数据
}

错误示例分析

你提到的错误代码:

func myBrokeFunc(tMap *map[string]int) {
    tMap["Goose"] = 14        // 错误:tMap是指针,不能直接索引
    *tMap["Gander"] = 14      // 错误:运算符优先级问题
}

第二个错误是由于运算符优先级:[]索引运算符的优先级高于*解引用运算符,所以*tMap["Gander"]被解析为*(tMap["Gander"]),而tMap是指针不能索引。

特殊情况:需要修改map变量本身

只有在需要修改map变量本身(如重新分配)时才需要传递指针:

func reassignMap(m *map[string]int) {
    *m = make(map[string]int)  // 修改指针指向的map变量
    (*m)["new"] = 1
}

总结

  • 大多数情况下直接传递map即可
  • 需要指针时,使用(*mapPtr)[key]语法或先解引用到临时变量
  • 这种设计避免了指针和索引运算符的歧义,保持了语法清晰性
回到顶部