英文:
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*
如何进行“显式”解引用呢?
英文:
So I am back with more beginner questions that I can not seem to wrap my head around.
I was experimenting with the following code.
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))
}
my intention was to assign the result of Sub() to diff in the following way
diff.Sub(powers[i+1], v)
however this results in seqDiffs's value being 1995 (the correct last value) repeated over and over. I know that this is likely because seqDiffs is just a list of pointers to the same memory address but what I dont understand is why the following works just fine
v.Sub(powers[i+1], v)
seqDiffs = append(seqDiffs, v)
this results in seqDiffs being a list of all the odd numbers from 3 to 1995 which is correct but isn't this essentially still a list of pointers to the same memory address as well?
Also why is the following correct when it should also result in seqDiffs being a list of pointers to the same memory address as well?
diff = v.Sub(powers[i+1], v)
seqDiffs = append(seqDiffs, diff)
also I tried to do it the following way
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)
}
but received these errors from the 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*
How would I make an "explicit" dereference?
答案1
得分: 2
在调试Go语言中指针问题时,了解情况的一种方法是使用fmt.Printf
和%p
来打印感兴趣的变量的内存地址。
关于你的第一个问题,为什么将diff.Sub(powers[i+1], v)
的结果追加到*big.Int
切片中会导致每个索引都是相同的值 - 这是因为你更新了diff
所分配的内存地址上的值,并将该指针的副本追加到切片中。因此,切片中的所有值都是指向相同值的指针。
打印diff
的内存地址将显示这一点。在填充切片后,可以像这样进行操作:
for _, val := range seqDiffs {
fmt.Printf("%p\n", val) // 当我运行这段代码时,每次迭代都打印出0xc4200b7d40
}
在你的第二个示例中,值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) // 第一次迭代打印0xc4200109e0,第二次打印0xc420010a00,第三次打印0xc420010a20,以此类推
seqDiffs = append(seqDiffs, diff)
}
关于你的第二个问题 - 在Go语言中,使用new
关键字会为指定类型分配内存,但不会对其进行初始化(请参考文档)。在你的情况下,调用new
会为指向big.Int
的指针类型(**big.Int
)分配内存,因此编译器会报错,说你不能在append
的调用中使用这种类型。
为了显式地解引用diff
以调用其上的Sub
方法,你需要修改代码如下:
(*diff).Sub(powers[i+1], v)
在Go语言中,选择器表达式会自动解引用结构体指针,但在这种情况下,你正在对指向指针的指针调用方法,因此你必须_显式_解引用它。
关于在Go语言中调用结构体方法(选择器表达式)的详细信息,可以在这里找到非常有用的阅读材料。
将其添加到切片中的代码如下:
seqDiffs = append(seqDiffs, *diff)
英文:
When debugging issues with pointers in Go, one way to understand what is going on is use fmt.Printf
using %p
to print the memory address of variables of interest.
In regards to your first question as to why when appending the results of diff.Sub(powers[i+1], v)
to your slice of *big.Int
results in a slice where every index is the same value - you are updating the value at the memory address diff
is assigned to and appending a copy of that pointer to the slice. Thus all values in the slice are pointers to the same value.
Printing the memory address of diff
will show this to be the case. After populating your slice - doing something like the following:
for _, val := range seqDiffs {
fmt.Printf("%p\n", val) // when i ran this - it printed 0xc4200b7d40 every iteration
}
In your second example, the the value v
is pointer to a big.Int
at a different address. You are assigning the the result of v.Sub(..)
to diff
, which updates the underlying address diff is pointing to. So when you append diff
to your slice, you are appending a copy of of a pointer at a unique address. Using fmt.Printf
you can see this like so -
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)
}
Regarding your second question - using the new
keyword in Go allocates memory of the specified type but does not initialize it (check the docs). A call to new
in your case allocates a type of pointer to a pointer to a big.Int
(**big.Int
), thus the compiler error saying you cannot use this type in your call to append
.
To explicitly dereference diff
in order to call the Sub
on it, you would have to modify your code to the following:
(*diff).Sub(powers[i+1], v)
In Go, a selector expression dereferences pointers to structs for you, but in this case you are calling a method on a pointer to a pointer, thus you have to explicitly dereference it.
A very informative read on calling methods on structs (selector expressions) in Go can be found here
And to add it to the slice
seqDiffs = append(seqDiffs, *diff)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论