Pointers 为什么';不允许获取映射值的地址?

Pointers 为什么';不允许获取映射值的地址?,pointers,dictionary,go,Pointers,Dictionary,Go,这与相同,但我对公认的答案不满意:“切片由备份数组支持,而贴图则不是。” 注:我现在已对参考问题添加了我自己的答案 上面 这个问题甚至更好,但其公认的答案是,您不能修改映射中结构值的字段,因为您不能获取其地址(这是我的问题) 映射由内存结构(可能包括数组)支持,就像切片一样 那么,我不能获取映射值的地址的真正原因是什么 我想修改一个映射结构值。地图中的数值可以使用++或+= func icandothis() { cmap := make(map[int]complex

这与相同,但我对公认的答案不满意:“切片由备份数组支持,而贴图则不是。”

注:我现在已对参考问题添加了我自己的答案 上面

这个问题甚至更好,但其公认的答案是,您不能修改映射中结构值的字段,因为您不能获取其地址(这是我的问题)

映射由内存结构(可能包括数组)支持,就像切片一样

那么,我不能获取映射值的地址的真正原因是什么

我想修改一个映射结构值。地图中的数值可以使用++或+=

     func icandothis() {
        cmap := make(map[int]complex64)
        cmap[1] += complex(1, 0)
        fmt.Println(cmap[1])
     }
 func icandothis() {
    cmap := make(map[int]complex64)
    cmap[1] += complex(1, 0)
    fmt.Println(cmap[1])
 }
但不能修改结构值:

type Complex struct {
    R float32
    I float32
}

func (x *Complex) Add(c Complex) {
    x.R += c.R
    x.I += c.I
}

func but_i_cannot_do_this() {
    cmap := make(map[int]Complex)
    //cmap[1].Add(Complex{1, 0})
    fmt.Println(cmap[1])

}

func so_i_have_to_do_this() {
    cmap := make(map[int]Complex)
    c := cmap[1]
    c.Add(Complex{1, 0})
    cmap[1] = c
    fmt.Println(cmap[1])

}

让我们从这个误解开始:

我想修改一个映射结构值。地图中的数值 可以使用++或+=

     func icandothis() {
        cmap := make(map[int]complex64)
        cmap[1] += complex(1, 0)
        fmt.Println(cmap[1])
     }
 func icandothis() {
    cmap := make(map[int]complex64)
    cmap[1] += complex(1, 0)
    fmt.Println(cmap[1])
 }

让我们扩展速记形式:

package main

import (
    "fmt"
)

func icandothisShort() {
    cmap := make(map[int]complex64)
    cmap[1] += complex(1, 0)
    fmt.Println(cmap[1])
}

func icandothisLong() {
    cmap := make(map[int]complex64)
    // An assignment operation x op= y where op is a binary arithmetic operator
    // is equivalent to x = x op (y) but evaluates x only once.
    // cmap[1] += complex(1, 0)
    v := cmap[1]          // v = zero value = complex(0, 0)
    v = v + complex(1, 0) // v = complex(0, 0) + complex(1, 0) = complex(1, 0)
    cmap[1] = v           // cmap[1] = v = complex(1, 0)
    a := cmap[1]          // a = complex(1, 0)
    fmt.Println(a)        // complex(1, 0)
}

func main() {
    icandothisShort()
    icandothisLong()
}
游乐场:

输出:

(1+0i)
(1+0i)
正如您在
icandothisLong()
中看到的,
icandothisShort()
的扩展形式中所示,没有任何更新



下一个误解

映射仅由内存结构(可能包括数组)支持 就像切片一样

那么,我不能获取地图地址的真正原因是什么 价值


映射由bucket内存结构支持。映射键通过一个不完善的动态散列来标识当前主存储桶。映射键和值存储在主存储桶或溢出存储桶中。创建、更新和删除地图条目时,地图存储桶会不断重新组织。 地图条目在内存中没有固定位置

以下是地图工作原理的详细说明:


因为另一种选择是不使用
删除功能。考虑下面的例子。

m := map[int]int{1: 2}
v := &m[1]
delete(m, 1)
v
指向什么

有四个可能的答案(好吧,更多,但它们同样糟糕),没有一个是令人满意的

  • 一块墓碑,标明丢失的条目。这将不允许在从哈希表中删除条目后重用该条目,因为哈希表需要更常见的大小调整,并且会在经常删除元素的映射中浪费内存

  • 悬空指针,与Go内存安全要求不兼容

  • 这是一个无效代码。需要对所有指针访问进行运行时检查,或者实现Rust样式的借用检查器

  • 要求值位于添加间接寻址的指针后面。您可以使用
    map[int]*int
    等类型自己完成

您可以说,程序不应该在映射元素被删除后访问它的指针。这在内存中是不安全的编程语言,而Go不是


顺便说一句,出于引用目的,在允许引用映射元素的同时,其他映射操作肯定可以实现。一个遵循C++迭代器失效要求的MAP实现将容易获得-虽然有成本,因为C++映射相对较慢。但是如果大多数C++程序都能管理这个费用,那么可以去.< /p>因为地图可以自由地重新排列它们的内容,这意味着在地图被修改之后,指针可能不再指向它创建时所做的相同值。然而,由于整个问题是你的方法接收器是指针类型的…为什么不在地图上存储指针呢?重复。懒得搜索。我知道这个问题是重复的。我在问题中提到这一点。问题的关键是,我对原始问题中已被接受的答案不满意,因此再次提出问题,并对之前的答案表示反对。@Adrian,我认为你的第一个评论是正确的答案:地图可以动态地重新安排其内容,而不是一个永远不会这样做的片段。一片不长。因此,该语言的设计者可能认为,程序员不使用指向某个值的指针会更安全,该值可能会在非他们选择的时间发生变化,或者让map实现自由地将重新定位的内存重新用于除相同类型的值之外的其他内容。感谢@peterSO您的声明
这是一个错误的声明
是绝对正确的。upvoteI的错误在于cmap[1]+=complex(1,0)不会“修改”映射元素,但是一个好的编译器实现不应该像规范所说的那样对cmap[1]进行两次计算。至于我对map实现的无知,对于垃圾收集实现更是如此,我不会接受我的无知作为答案。感谢map实现者的视频,他回答了这个问题(部分):“它会干扰这个增长过程,因此如果我获取bucket中某个条目的地址,然后我将该条目保留很长时间,同时地图会增长,那么指针突然指向一个旧的bucket而不是新的bucket,并且该指针现在无效,因此,如果不限制grow的工作方式,很难提供在映射中获取值地址的功能。。。C++以不同的方式增长,因此你可以取桶的地址(21:45)。我接受这个答案是因为映射实现者引用了对话。映射中已删除值的指针应该指向当时存储在该位置的任何有效值,可能是零值。它不必是原始值。就像指向切片元素的指针可能指向除origi之外的其他内容一样如果元素已被替换,则将其设置为其他值。