在Go中可以使用什么指针?

时间:2014-06-09 12:54:55

标签: pointers go

我想我明白指针是什么,但我不太明白何时使用它 以下代码段来自"A Tour of Go" " * Vertex"的目的是什么?和"& Vertex"?
我用" Vertex"替换了它们。它运行正常。

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}

2 个答案:

答案 0 :(得分:5)

这不是指针/值区别的一个特别好的例子,因为在那种情况下它们可以互换!当您需要变异数据时,指针非常有用。#34;远程" (来自另一个职能)。

func (v Vertex) SetX(x int) {
    v.X = x
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v)
    v.SetX(1)
    fmt.Println(v)
}

正如你所注意到的,这并没有改变任何东西(严格来说,它改变了顶点的副本,但在大多数情况下它只是语义)! v的值仍为{3,4}。尝试改为:

func (v *Vertex) SetX(x int) {
    v.X = x
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v)
    v.SetX(1)
    fmt.Println(v)
}

突然,它有效,第二次打印{1,4}。现在,如果您有点好奇,可以决定尝试并将v := &Vertex{3, 4}更改为v := Vertex{3, 4}。实际上,上面的片段仍然有用。奇怪。同样,如果您更改第二个代码段中的同一行以包含指针,它也会以相同的方式工作。

为什么呢? Go已经"透明"指针。在具有显式指针值(如C或C ++)的其他语言中,您必须显式使用运算符&*来取消引用指针。 C和C ++甚至在字段访问和方法调用v->SetX上都有特殊的指针追踪语法。

无论好坏,Go都会隐藏此信息。如果你有一个值并且需要调用指针方法,那么Go会很乐意为你做(&v).Method(),如果你需要取消引用来调用一个值方法,它会很自然地(*v).Method()。在大多数情况下都是如此,有一些像地图这样的事情并不适用的极端情况,但总的来说这是适用的。

所以,当它归结为它时,你何时应该在方法上使用指针接收器?答案,实际上,大多数情况下是#34;" Go Style Guide通常建议使用指针类型方法接收器,除非接收器是mapfuncchan的直接别名,它是一个切片,它不是&{ #39;需要重新设置,或者你对小的不可变数据类型进行优化(因为指针追逐比复制慢一点)。我补充一点,你通常不应该使用直接指针指针。

通常,当您不知道使用哪个时,请使用指针接收器。 99%的时间使用指针将为您提供所期望的行为,特别是如果您习惯使用Python或C#等语言。错误地使用指针导致错误比较罕见,比较获取错误的可能性,因为您的Setter方法实际上没有设置任何东西。

答案 1 :(得分:2)

这个特殊的例子很糟糕,因为在指针类型*Vertex上定义的方法不会尝试改变其接收者的值(调用该方法的值)。 / p>

在Go中,所有内容都通过值传递/分配 - 包括指针。所以,当你有一个方法

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

(注意接收者的类型规范中*前面没有Vertex),它可以正常工作,因为当你这样做时

v := Vertex{2, 3}
x := v.Abs()

v调用网站v.Abs()的值已复制Abs()方法收到的值。

现在假设您想要使用方法调用更改(变更)某些Vertex变量。一种天真的方法,比如,

func (v Vertex) SetX(x float64) {
    v.X = x
}

v := Vertex{2, 3}
v.SetX(-5)
// Here, v.X is still 2

无法正常工作,因为它会更改X的值v,而X已在调用时复制到被调用者;该方法更改了副本的func (v *Vertex) SetX(x float64) { v.X = x } v := Vertex{2, 3} v.SetX(-5) - 仅在方法范围内看到的更改。

另一方面,如果你要在指针上定义该方法(它将一个实际值的地址而不是一个值本身),那就可以了:

v

这里,编译器将调用点SetX()处{{1}}的地址并将其传递给方法。然后,该方法将使用该地址来引用调用者范围中的值。

语法混淆是因为Go(在大多数情况下)允许您不使用运算符来获取值的地址并取消引用该地址。

如果您来自PHP,Python等流行语言之一,那么主要区别在于其中许多对象都是特殊的"并始终通过引用传递。 Go更低级,并且不会在程序员的后面使用魔法,所以你必须明确是否要将指针或值的副本传递给方法。

请注意,这不仅仅是方法是否能够改变其接收者;表演事物也可能在这里起作用。