为什么指针类型的行为与结构类型不同?

时间:2018-12-05 10:13:22

标签: pointers go struct

我有这段Go代码片段,其中我试图分别使用两个函数ff2来更改常规int和结构中int的值。我不明白为什么我需要做*i来更改int的值,但是当我在结构中更改X的值时我不需要这样做。

type Point struct {
    X int
}

func t(i *int) {
    *i = 20
}

func t2(p *Point) {
    p.X = 200
}

func main() {
    g := 30
    t(&g)
    fmt.Println(g)

    p := Point{3}
    t2(&p)
    fmt.Println(p)
}

2 个答案:

答案 0 :(得分:3)

考虑这两个函数的最简单方法是在t2函数中,使用指向基础结构的指针来更改结构的字段。在t函数中,您正在更改整个基础对象(整数)。

实际上,您可以写p.X的事实实际上只是一件好事。在C之类的语言中,如果要对非指针变量进行操作,则只能使用p.X。对于指针,您必须使用p->X来表示您正在使用间接访问字段,或者实际上是取消引用指针((*p).X)。

在内部,go仍然做同样的事情,它只允许您省略显式的取消引用,并且不需要间接运算符。

但是,两个函数都是等效的。 Point是一个结构,具有一个或多个字段。另一种类型是*int,一个int是单个标量值。要使t2等价(并重新分配整个基础对象),您必须将代码更改为与在*int情况下必须执行的代码相同:

func t2(p *Point) {
    *p = Point{
         X: 200,
         Y: p.Y,
     }
}

根据以下注释:TL; DR版本是,如果您访问结构类型的字段之一,则不必显式取消对结构类型的指针的引用。您必须在C / C ++中做到这一点,但是go编译器会为您解决这一问题。可以看出,您使用的是指针类型的变量,并且以与C编译器编译p.X相同的方式编译p->X。因此,您无需显式取消引用p

如果将*p.X声明为:

,您仍然必须写Point
type Point struct {
    X *int
}

因为表达式p.X的计算结果为类型*int的操作数,因此需要对其进行相应的处理。

答案 1 :(得分:2)

由于i是类型*int的指针,并且您要修改指向的对象,因此必须编写*i

相同的语法也适用于结构,例如您可以编写(*p).X,但这是一项经常性的操作,因此该规范允许使用p.X,这意味着(*p).X,没有歧义,并且是方便的快捷方式。

它在Spec: Selectors:

  

作为例外,如果x的类型是defined指针类型,并且(*x).f是表示字段(但不是方法)的有效选择器表达式,则{{1} }是x.f的简写。