函数中结构的指针和引用

时间:2019-01-31 14:55:50

标签: go

我从Go开始,在理解函数内部结构的指针和引用时遇到困难。

考虑示例https://play.golang.org/p/zd8La4ecNXw

package main

import "fmt"

type User struct {
  Name string
}

func main() {
  // 1st
  u := User{Name: "Anne"}
  fmt.Println("1st: ", &u.Name)
  fmt.Println("1st: ", u.Name)
  Modify1(u)
  fmt.Println("1st: ", u.Name)

  // 2nd
  fmt.Println()
  v := &User{Name: "Anne"}
  fmt.Println("2nd: ", &v.Name)
  fmt.Println("2nd: ", v.Name)
  Modify2(v)
  fmt.Println("2nd: ", v.Name)

  // 3rd
  fmt.Println()
  y := &User{Name: "Anne"}
  fmt.Println("3rd: ", &y.Name)
  fmt.Println("3rd: ", y.Name)
  Modify3(&y)
  fmt.Println("3rd: ", y.Name)

  // 4th
  fmt.Println()
  z := &User{Name: "Anne"}
  fmt.Println("4th: ", &z.Name)
  fmt.Println("4th: ", z.Name)
  Modify4(z)
  fmt.Println("4th: ", z.Name)
}

func Modify1(u User) {
  fmt.Println("func: ", &u.Name)
  u.Name = "Duncan"
  fmt.Println("func: ", u.Name)
}

func Modify2(v *User) {
  fmt.Println("func: ", &v.Name)
  v = &User{Name: "Paul"}
  fmt.Println("func: ", v.Name)
}

func Modify3(y **User) {
  fmt.Println("func: ", &y)
  fmt.Println("func: ", &(*y).Name)
  *y = &User{Name: "Bob"}
  fmt.Println("func: ", (*y).Name)
}

func Modify4(z *User) {
  fmt.Println("func: ", &z.Name)
  z.Name = "John"
  fmt.Println("func: ", z.Name)
}

结果:

1st:  0x40e128
1st:  Anne
func:  0x40e140
func:  Duncan
1st:  Anne

2nd:  0x40e158
2nd:  Anne
func:  0x40e158
func:  Paul
2nd:  Anne

3rd:  0x40e188
3rd:  Anne
func:  0x40e198
func:  0x40e188
func:  Bob
3rd:  Bob

4th:  0x40e1b8
4th:  Anne
func:  0x40e1b8
func:  John
4th:  John

除了 1st 示例,在这里我没有任何疑问,所有其他问题似乎都指向原始结构分配,但是 2nd 不会更改调用者的值。

为什么会发生这种情况,为什么与 3rd 4th 不同?

1 个答案:

答案 0 :(得分:3)

要记住的关键是,所有内容都是通过值传递的(也就是通过复制传递)。当您将指针传递给函数时,该参数是该函数中的 still 一个局部变量,其中包含指针的副本。该本地副本指向调用者引用的相同内存。

执行此操作时:

v = &User{Name: "Paul"}

您正在使用指向v new 实例的 new 指针覆盖局部变量 User 。现在它指向的内存与调用方的指针不同,因此调用方看不到任何内容。

如果您改为这样做:

*v = User{Name: "Paul"}

然后指向 same 内存的 same 指针将被User new 实例覆盖。 / p>

同样,在Modify3中,您有一个指向指针的指针。因此,您的调用者和函数每个都有一个局部变量,该局部变量指向一个存储位置,该存储位置保存另一个可以找到实际值的存储位置。请注意,这是Go语言中非常不常见(但并非闻所未闻)的模式,但在其他语言中却很常见。当您这样做时:

*y = &User{Name: "Bob"}

您正在创建指向新User的新指针,并将该新指针存储在外部指针所指向的共享内存中。因此,再次,函数和调用者都共享内存,并且两者都将看到更改。