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

huangapple go评论70阅读模式
英文:

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 &lt; 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(&quot;%p\n&quot;, 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(&quot;%p\n&quot;, 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)

huangapple
  • 本文由 发表于 2017年5月20日 23:13:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/44087509.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定