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

时间:2017-05-20 15:13:15

标签: pointers go variable-assignment biginteger

所以我回来时有更多的初学者问题,我似乎无法解决这个问题。 我正在尝试以下代码。

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*

我如何进行“明确”的解除引用?

1 个答案:

答案 0 :(得分:2)

在使用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)的指针的类型,因此编译器错误说你不能在调用{{{ 1}}。

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

Sub

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

可以找到关于Go中结构(选择器表达式)的调用方法的非常有用的信息here

并将其添加到切片

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