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)
所以我带着更多初学者的问题回来了,这些问题我似乎无法全神贯注。 我正在试验以下代码。
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)