Golang指针

时间:2013-02-26 14:31:55

标签: pointers go

我目前正在学习用Go语言编程。 我在理解Go指针方面遇到了一些困难(现在我的C / C ++还很远......)。 例如,在Tour of Go#52(http://tour.golang.org/#52)中,我读到了:

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())
}

但如果不是

func (v *Vertex) Abs() float64 {
[...]
v := &Vertex{3, 4}

我写道:

func (v Vertex) Abs() float64 {
[...]
v := Vertex{3, 4}

甚至:

func (v Vertex) Abs() float64 {
[...]
v := &Vertex{3, 4}

反之亦然:

func (v *Vertex) Abs() float64 {
[...]
v := Vertex{3, 4}

我得到了完全相同的结果。是否存在差异(记忆方式等)?

5 个答案:

答案 0 :(得分:33)

您的示例使用的Go语言有两种不同的规则:

  1. 可以从具有值接收器的方法导出具有指针接收器的方法。因此func (v Vertex) Abs() float64将自动生成其他方法实现:

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

    编译器将自动找到生成的方法:

    v := &Vertex{3, 4}
    v.Abs()  // calls the generated method
    
  2. Go可以自动获取变量的地址。在以下示例中:

    func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X+v.Y*v.Y) }
    func main() {
        v := Vertex{3, 4}
        v.Abs()
    }
    

    表达式v.Abs()等同于以下代码:

    vp := &v
    vp.Abs()
    

答案 1 :(得分:13)

存在差异。例如,非指针接收器形式强制该方法在副本上工作。这样,该方法无法改变它所调用的实例 - 它只能访问该副本。例如,这可能是无效的。时间/记忆表现/消费等。

OTOH,带指针接收器的实例和方法指针允许在需要的地方轻松实例共享(和变异)。

更多详情here

答案 2 :(得分:2)

不同之处在于传递参考与传递价值。

func f(v Vertex)中,参数被复制到参数v中。在func f(v *Vertex)中,传递了指向现有Vertex实例的指针。

使用方法时,可以为您完成一些解除引用,因此您可以使用方法func (v *Vertex) f()并在不先取指针的情况下调用它:v := Vertex{...}; v.f()。这只是一种语法糖,AFAIK。

答案 3 :(得分:0)

这些例子有两个主要区别:

func (v *Vertex) Abs()....

接收器将为传递参考为v,你只能在指针上调用此方法:

v := Vertex{1,3}
v.Abs() // This will result in compile time error
&v.Abs() // But this will work

另一方面

func (v Vertex) Abs() ....

您可以在指针和结构上调用此方法。即使您在指针上调用此方法,接收器也将按值传递

v := Vertex{1,3}
v.Abs() // This will work, v will be copied.
&v.Abs() // This will also work, v will also be copied.

您可以声明func (v *Vertex)func (v Vertex)

答案 4 :(得分:0)

正如规范所说

  

如果方法集x(类型)x包含m并且参数列表可以分配给m的参数列表,则方法调用x.m()有效。如果x是可寻址的并且& x的方法集包含m,则x.m()是(& x).m()的缩写:

在你的情况下,如果方法是可寻址的,v.Abs()是& v.Abs()的简写。