指针传递与值传递
严格地说,go方法或函数只有一种传递方式,那就是值传递。每次将一个变量作为参数传递时,都会创建一个新的变量副本并将其传递给所调用的函数或方法。副本分配在不同的内存地址。
在指针传递变量的情况下,将创建指向相同内存地址的新副本。为了感受它们之间的差异,我们来看看它是如何工作的。
值传递
package main
import "fmt"
type User struct {
name string
age int
}
func changeUser(u User) {
u.name = "李四"
u.age = 33
}
func main() {
user := User {
name: "张三",
age: 22,
}
changeUser(user)
fmt.Println(user)
}
接下来我们运行代码,得到的结果是 {张三 22}
请注意,即使changeUser 函数将u的name改为张三,age改为33.但是不会影响main函数里的user
因为u是user的值拷贝,内存地址并不相同。即user和u是两个不同的User
指针传递
package main
import "fmt"
type User struct {
name string
age int
}
func changeUser(u *User) {
u.name = "李四"
u.age = 33
}
func main() {
user := User {
name: "张三",
age: 22,
}
changeUser(&user)
fmt.Println(user)
}
再次运行代码,得到的结果是{李四 33}
发生这种情况是因为传入changeUser函数的u是user的内存地址拷贝,即&user
u和&user指向的内存地址是一样的,所以它们两个是同一个User
何时使用值传递?何时使用指针传递?
- 变量是slice、map结构时使用值传递,因为slice和map本身就是引用类型
- 如果需要修改结构体属性值,则必须使用指针
- 结构不大,并且只读情况下,尽量使用结构体。因为有逃逸分析
- 如果结构体占用内存较大,结构体作为参数传递时会发生拷贝影响性能,所以需要使用指针
在Go中值传递可能比指针传递开销更小
发生这种情况是因为Go使用逃逸分析来确定变量是否可以安全地分配到函数的栈帧上,这可能比在堆上分配变量开销小的多。通过值传递可以简化Go中的逃逸分析,并为变量提供更好的分配机会。
网友评论