对比 Go 中传址传参和传值传参
Go 参数可以传指针(引用)和传值。本文将比较这两种方式,着重对比二者的不同点。
传指针 vs 传值
严格来说,Go 中传参数只有一种形式 - 传值。变量当作参数传递时,变量会被复制,这个复制值再传递到函数或者方法中。这个复制值存放在不同的内存地址中。
当一个变量当作指针传递时,指向相同内存地址的指针得到复制。通过例子来看看其中的不同。
传值
package main
import "fmt"
type Person struct {
firstName string
lastName string
}
func changeName(p Person) {
p.firstName = "Bob"
}
func main() {
person := Person {
firstName: "Alice",
lastName: "Dow",
}
changeName(person)
fmt.Println(person)
}
运行得到:
{Alice Dow}
注意到虽然 changeName 函数改变 firstName 为 “Bob” ,但是这并没有改变 main 函数中 person 变量。这是因为 changeName 函数改变的是 person 变量的拷贝值,而不是它自身。
传指针
package main
import "fmt"
type Person struct {
firstName string
lastName string
}
func changeName(p *Person) {
p.firstName = "Bob"
}
func main() {
person := Person {
firstName: "Alice",
lastName: "Dow",
}
changeName(&person)
fmt.Println(person)
}
运行得到:
{Bob Dow}
示例中,main 函数中的 person 变量被 changeName 函数修改了。这是因为 &person 和 p 是两个结构相同并且存储的内存地址也相同的指针。
如何选择
通常根据应用场景来选择。下来来看看常见的使用案例。
变量不变
保持变量不变,只能传值。这样变量不会被下游的程序修改。反之,如果希望改变变量的值,那就传指针。
变量拥有大的数据结构
如果变量拥有大的数据结构,同时对性能有要求。那么优先选择传指针。这样可以避免在内存中复制整个数据结构。
变量是 map 或 slice
Maps 和 slices 在 Go 里面是引用类型,应该传值。
传值通常代价更小
尽管 Go 有点像 C,但是它的编译过程不同。C 特征不都能应用到 Go 上。Go 中传值可能比传指针代价更小。因为 Go 使用逃逸分析了来确定变量能否被安全存储在函数的栈中,这比存储在堆中代价更小。传值简化了逃逸分析,让其更容易存储在栈中。
结论
如何传递变量通常由变量类型和使用场景决定的。否则,建议传值。同时维持团队内选择的一致性也很重要,避免造成混乱。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: