Golang:帮助理解指针、赋值和意外行为

Golang: help understanding pointers, assignment, and unexpected behavior

所以我带着更多初学者的问题回来了,这些问题我似乎无法全神贯注。 我正在试验以下代码。

func main() {
start := time.Now()
var powers []*big.Int
for i := 1; i < 1000; i++ {
    I := big.NewInt(int64(i))
    I.Mul(I, I)
    powers = append(powers, I)
}
fmt.Println(powers)
fmt.Println(time.Since(start))
start = time.Now()
var seqDiffs []*big.Int
diff := new(big.Int)
for i, v := range powers {
    if i == len(powers)-2 {
        break
    }
    diff = v.Sub(powers[i+1], v)
    seqDiffs = append(seqDiffs, diff)
}
fmt.Println(seqDiffs)
fmt.Println(time.Since(start))
}

我的意图是按以下方式将 Sub() 的结果分配给 diff

diff.Sub(powers[i+1], v)

然而,这导致 seqDiffs 的值为 1995(正确的最后一个值)一遍又一遍地重复。我知道这很可能是因为 seqDiffs 只是指向相同内存地址的指针列表,但我不明白的是为什么下面的工作正常

v.Sub(powers[i+1], v)
seqDiffs = append(seqDiffs, v)

这导致 seqDiffs 成为从 3 到 1995 的所有奇数的列表,这是正确的,但这本质上不是仍然是指向相同内存地址的指针列表吗? 另外,为什么以下是正确的,它也应该导致 seqDiffs 也是指向相同内存地址的指针列表?

diff = v.Sub(powers[i+1], v)
seqDiffs = append(seqDiffs, diff)

我也试过用下面的方法

diff := new(*big.Int)
for i, v := range powers {
if i == len(powers)-2 {
    break
}
diff.Sub(powers[i+1], v)
seqDiffs = append(seqDiffs, diff)
}

但从 ide 收到这些错误:

*./sequentialPowers.go:26: calling method Sub with receiver diff (type **big.Int) requires explicit dereference
./sequentialPowers.go:27: cannot use diff (type **big.Int) as type *big.Int in append*

我将如何进行 "explicit" 取消引用?

在调试 Go 中的指针问题时,了解正在发生的事情的一种方法是使用 fmt.Printf 使用 %p 打印感兴趣变量的内存地址。

关于你的第一个问题,为什么将 diff.Sub(powers[i+1], v) 的结果附加到 *big.Int 的切片会导致每个索引值相同的切片 - 你正在更新值在内存地址 diff 分配给该指针的副本并将其附加到切片。因此,切片中的所有值都是指向相同值的指针。

打印 diff 的内存地址将证明是这种情况。填充切片后 - 执行以下操作:

for _, val := range seqDiffs {
    fmt.Printf("%p\n", val) // when i ran this - it printed 0xc4200b7d40 every iteration
}

在您的第二个示例中,值 v 是指向不同地址的 big.Int 的指针。您正在将 v.Sub(..) 的结果分配给 diff,这会更新 diff 指向的基础地址。因此,当您将 diff 附加到切片时,您是在唯一地址处附加了一个指针的副本。使用 fmt.Printf 你可以这样看 -

var seqDiffs []*big.Int
diff := new(big.Int)
for i, v := range powers {
    if i == len(powers)-2 {
        break
    }
    diff = v.Sub(powers[i+1], v)
    fmt.Printf("%p\n", diff) // 1st iteration 0xc4200109e0, 2nd 0xc420010a00, 3rd 0xc420010a20, etc
    seqDiffs = append(seqDiffs, diff)
} 

关于你的第二个问题 - 在 Go 中使用 new 关键字分配指定类型的内存但不初始化它 (check the docs)。在您的情况下,对 new 的调用会将一种类型的指针分配给指向 big.Int (**big.Int) 的指针,因此编译器错误提示您不能在对 [= 的调用中使用此类型30=].

要显式取消引用 diff 以便在其上调用 Sub,您必须将代码修改为以下内容:

(*diff).Sub(powers[i+1], v)

在 Go 中,选择器表达式为您取消引用指向结构的指针,但在这种情况下,您是在指向指针的指针上调用方法,因此您必须 明确地 取消引用它。

可以找到有关在 Go 中调用结构(选择器表达式)方法的非常有用的读物​​ here

并将其添加到切片

seqDiffs = append(seqDiffs, *diff)