【问题标题】:go's big.Int underlying value mutates when value points to another instance当 value 指向另一个实例时,go 的 big.Int 基础值会发生变化
【发布时间】:2020-03-23 15:38:36
【问题描述】:

当将一个 big.Int 的实例指向另一个实例时,我遇到了 go 的 big.Int 的一些无法解释的行为。 我知道,为了将bit.Int 的实例的值设置为另一个,必须使用Int.SetXXX 设置器,因为它们实际上导致big.Int 中的底层abs 切片被复制到一个新的分配的数组。但是,暂时搁置一下,我想知道为什么会发生以下行为。

考虑以下几点:

错误示例(基础值发生变化):

func main() {
    v1p := big.NewInt(1)
    v2p := big.NewInt(2)

    v1 := *v1p
    v2 := *v2p

    v2 = v1

    v1.SetInt64(3)

    fmt.Println(v1.Int64(), v2.Int64())
}

(在此处运行:https://play.golang.org/p/WxAbmGdKG9b)

正确示例(值不会发生变化):

func main() {
    v1p := big.NewInt(1)
    v2p := big.NewInt(2)

    v1 := *v1p
    v2 := *v2p

    v2.Set(v1p)

    v1.SetInt64(3)

    fmt.Println(v1.Int64(), v2.Int64())
}

(在此处运行:https://play.golang.org/p/16qsGhwHIWf)

如果我理解正确,以下内容应该基本上说明了错误示例中会发生什么:

func main() {
    var a, b *int // analogous to the 2 big.Int pointers returned
    c, d := 3, 3
    a = &c // we set the pointers to point to something we can then dereference
    b = &d

    e := *a // e and f should now point to the values pointed to by the pointers
    f := *b

    // the rest is self-explanatory
    e = f

    c = 5
    d = 4

    fmt.Println(a, b, c, d, e, f)
}

(在此处运行:https://play.golang.org/p/cx76bnmJhG7)

我唯一的假设是,在 Wrong 示例中以某种方式将结构内容复制到 v2 时,abs 切片不会被深度复制,而是它的存储引用实际上与v1 中的切片指向的存储相同。

真的会这样吗?这也是符合语言规范的预期行为吗?

【问题讨论】:

  • 在第一个示例中,v1 和 v2 都将在 v2 = v1 之后引用 v1p,因此您没有改变 v2p,而是打印了两次 v1p。 (这是 100% 的猜测,因为我知道的很少)。见play.golang.org/p/GgdhXXNdB85
  • 你永远不应该取消引用*big.Int,所以这永远不会成为问题。 big.Int 包含一个内部切片(这意味着一个指针),这就是您要复制的内容。
  • 将v1 分配给v2 会复制结构值,其中包括nat 字段,它是一个切片。切片是类似结构的标头,包含指向后备数组的指针。复制切片只是复制标题,包括指针,并且副本将指向同一个后备数组。查看详情:Are golang slices passed by value? 和 How to inspect slice header?
  • 正如您正确猜测的那样,Go 中的任何副本都不是深层副本。任何包含切片、指针、通道或闭包的结构都不能被深度复制(除非它提供了这样做的函数/方法)。
  • 展望未来,发生的事情可能取决于实现。由于您现在有 2 个结构值(不是指针),因此调用一个对其进行变异的方法,这 2 个结构值可能不一致。您调用该方法的那个可能会更新切片元素和其他结构字段。其他更改的结构字段不会反映在其他结构上,切片元素的更改可能会也可能不会(例如,如果切片使用append() 扩展,则会创建一个新的切片头但其他结构不会看到这一点;但是如果当前元素发生变化,那将是)。

标签: pointers go


【解决方案1】:

正如 icza 和 Volker 所指出的,因为当取消引用 big.Int 指针时,实际值切片头结构被复制,指向相同的底层值,结果行为是同一个底层数组被多个切片引用,导致一个改变另一个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 2023-03-08
    • 1970-01-01
    • 2020-08-24
    相关资源
    最近更新 更多