首页 > 解决方案 > 函数中结构的指针和引用

问题描述

我从 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

除了我没有问题的第一个示例之外,所有其他示例似乎都指向原始结构分配,但第二个不会更改调用者值。

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

标签: go

解决方案


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

当你这样做时:

v = &User{Name: "Paul"}

您正在使用指向实例的新指针覆盖局部变量 。它现在指向与调用者指针不同的内存,所以调用者什么也看不到。vUser

如果您改为这样做:

*v = User{Name: "Paul"}

然后,指向相同内存相同指针将被.User

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

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

您正在创建一个指向 new 的新指针User,并将该新指针存储在外部指针指向的共享内存中。所以,同样,函数和调用者都在共享内存,两者都会看到变化。


推荐阅读