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
除了我没有问题的第一个示例之外,所有其他示例似乎都指向原始结构分配,但第二个不会更改调用者值。
为什么会发生这种情况,为什么这与3rd和4th不同?
解决方案
要记住的关键是所有内容都是按值传递的(也就是通过复制传递)。当您将指针传递给函数时,该参数仍然是该函数中的局部变量,包含指针的副本。该本地副本指向调用者引用的同一内存。
当你这样做时:
v = &User{Name: "Paul"}
您正在使用指向新实例的新指针覆盖局部变量 。它现在指向与调用者指针不同的内存,所以调用者什么也看不到。v
User
如果您改为这样做:
*v = User{Name: "Paul"}
然后,指向相同内存的相同指针将被.User
同样,在 中Modify3
,您有一个指向指针的指针。所以你的调用者和函数都有一个局部变量,它指向一个内存位置,它保存另一个内存位置,在那里可以找到一个实际值。请注意,这是 Go 中非常不寻常(但并非闻所未闻)的模式,但在其他语言中很常见。当你这样做时:
*y = &User{Name: "Bob"}
您正在创建一个指向 new 的新指针User
,并将该新指针存储在外部指针指向的共享内存中。所以,同样,函数和调用者都在共享内存,两者都会看到变化。
推荐阅读
- excel - VBA VLookup 不返回任何结果,但我可以直接调用这些值
- java - Android 工作室 - Rider.getName()' 上的空对象引用
- mongodb - MongoDB:带参数的createCollection
- system-verilog - 在 Quartus Prime 的 SystemVerilog 中使用 $ceil 定义参数
- perl - perl 脚本是为哪个版本的 perl 编写的?
- c# - 使用计时器自动折叠树节点
- r - R中使用不同计算机的相对路径
- sql - 从列中删除数据并将此子字符串数据用于 WHERE 子句
- swift - 为什么使用结构会增加应用程序的二进制大小?
- eclipse - Eclipse egit:如何使远程分支本地化