Go允許通過指針(有時稱為引用)和值來傳遞參數(shù)。在這篇文章中,我們將比較兩種方法,特別注意可能影響選擇的不同情境祈惶。
指針傳遞與值傳遞
嚴格地說,go方法或函數(shù)只有一種傳遞方式扮匠,那就是值傳遞
行瑞。每次將一個變量作為參數(shù)傳遞時,都會創(chuàng)建一個新的變量副本并將其傳遞給所調(diào)用的函數(shù)或方法餐禁。副本分配在不同的內(nèi)存地址。
在指針傳遞變量的情況下突照,將創(chuàng)建指向相同內(nèi)存地址的新副本帮非。為了感受它們之間的差異,我們來看看它是如何工作的讹蘑。
值傳遞
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}
請注意末盔,即使函數(shù)changeName將firstName更改為“Bob”,但更改不會影響main函數(shù)中的變量person座慰。發(fā)生這種情況是因為函數(shù)changeName修改了變量person的一個副本陨舱,而不是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}
在這種情況下版仔,函數(shù)main中的變量person在函數(shù)changeName中被修改游盲。發(fā)生這種情況是因為&person和p是存儲在相同內(nèi)存地址的相同結構的兩個不同指針。
預定的選擇
有時選擇是由使用上下文預先確定的蛮粮。讓我們來看看最常見的用例益缎。
變量不能被修改
我們沒有其他的選擇,只能通過值傳遞然想。所以這個變量不能在下游修改莺奔。反之亦然,如果變量被期望修改变泄,它必須通過指針傳遞令哟。
變量是一個大的結構
如果變量是一個大的結構恼琼,性能是一個問題,最好是通過指針傳遞變量屏富。這樣可以避免在內(nèi)存中復制整個結構晴竞。
變量是一個map或slice
Go中的map和slice是引用類型,應該通過值傳遞役听。
值傳遞通常開銷更小
即使Go看起來有點像C颓鲜,它的編譯器工作方式也不同。C的類比并不總是和Go一起工作典予。在Go中值傳遞可能比指針傳遞開銷更小甜滨。發(fā)生這種情況是因為Go使用逃逸分析來確定變量是否可以安全地分配到函數(shù)的棧幀上,這可能比在堆上分配變量開銷小的多瘤袖。通過值傳遞可以簡化Go中的逃逸分析衣摩,并為變量提供更好的分配機會。
最后
有時如何通過變量的選擇是由變量類型或其用法預先確定的捂敌。否則艾扮,強烈建議按值傳遞變量。此外占婉,與使自己的選擇保持一致非常重要泡嘴,以免混淆自己和隊友。