一文帶你深入了解Golang中的參數(shù)傳遞機(jī)制
值傳遞(Pass by Value)和引用傳遞(Pass by Reference)是編程語(yǔ)言中兩種主要的參數(shù)傳遞方式,決定了函數(shù)調(diào)用過(guò)程中實(shí)參(實(shí)際參數(shù))如何影響形參(形式參數(shù))以及函數(shù)內(nèi)部對(duì)形參的修改是否會(huì)影響到原始實(shí)參。
什么是值傳遞 (Pass by Value)
在值傳遞中,當(dāng)函數(shù)被調(diào)用時(shí),實(shí)參的值會(huì)被復(fù)制一份,并將這個(gè)副本傳遞給對(duì)應(yīng)的形參,函數(shù)內(nèi)部對(duì)形參的操作不會(huì)改變實(shí)參的原始值。
優(yōu)點(diǎn):
- 安全,函數(shù)內(nèi)對(duì)參數(shù)的修改不會(huì)影響原始數(shù)據(jù)。
- 簡(jiǎn)單清晰好理解,函數(shù)可以隨意操作參數(shù)而不會(huì)影響外部的值。
缺點(diǎn):
創(chuàng)建副本可能導(dǎo)致額外的內(nèi)存消耗,特別是當(dāng)數(shù)據(jù)結(jié)構(gòu)較大時(shí)。
不能直接修改原始數(shù)據(jù),需要通過(guò)返回值或者使用指針/引用。
引用傳遞 (Pass by Reference)
在引用傳遞中,傳遞的是實(shí)參的內(nèi)存地址,而不是實(shí)際值。因此,函數(shù)內(nèi)部對(duì)形參的任何修改都會(huì)直接影響到原始實(shí)參的值。
優(yōu)點(diǎn):
- 節(jié)省內(nèi)存,因?yàn)闆](méi)有創(chuàng)建實(shí)際數(shù)據(jù)的副本。
- 在函數(shù)內(nèi)可以直接修改原始數(shù)據(jù)。
缺點(diǎn):
- 安全性降低,因?yàn)楹瘮?shù)內(nèi)部的修改會(huì)影響到函數(shù)外部的原始數(shù)據(jù)。
- 可能導(dǎo)致代碼難以理解和維護(hù),因?yàn)閿?shù)據(jù)可以在多個(gè)地方被修改。
Golang 中的參數(shù)傳遞方式
在 Go 語(yǔ)言中,所有的函數(shù)參數(shù)傳遞都是值傳遞(pass by value),當(dāng)將參數(shù)傳遞給函數(shù)時(shí),實(shí)際上是將參數(shù)的副本傳遞給函數(shù)。然而,這并不意味著在函數(shù)內(nèi)部對(duì)參數(shù)的修改都不會(huì)影響原始數(shù)據(jù)。因?yàn)樵?Go 中,有些數(shù)據(jù)類(lèi)型本身就是引用類(lèi)型,比如切片(slice)、映射(map)、通道(channel)、接口(interface)和指針(pointer)。當(dāng)這些類(lèi)型作為參數(shù)傳遞給函數(shù)時(shí),雖然傳遞的是值,但值本身就是一個(gè)引用。
基本類(lèi)型的值傳遞
基本類(lèi)型(如int、float、bool 和 string)的簡(jiǎn)單示例如下:
package main
import "fmt"
func modifyValue(x int) {
x = 100
}
func main() {
original := 1
modifyValue(original)
fmt.Println(original) // 輸出 1,未被修改
}
在上面的例子中,original 是一個(gè) int 類(lèi)型的變量,當(dāng)被傳遞到 modifyValue 函數(shù)時(shí),實(shí)際上是傳遞了它的副本。因此,在函數(shù)內(nèi)部對(duì) x 的修改并不會(huì)影響 original 的值。
切片的“引用”傳遞
看一個(gè)切片的例子,來(lái)理解下雖然是值傳遞,但看起來(lái)像是引用傳遞的情況。簡(jiǎn)單示例代碼如下:
package main
import "fmt"
func modifySlice(s []int) {
s[0] = 100
}
func main() {
originalSlice := []int{1, 2, 3}
modifySlice(originalSlice)
fmt.Println(originalSlice) // 輸出 [100, 2, 3],第一個(gè)元素被修改
}
在這個(gè)例子中,盡管 originalSlice 作為一個(gè)值傳遞給了 modifySlice 函數(shù),但是這個(gè)值實(shí)際上是一個(gè)切片的引用。切片內(nèi)部包含一個(gè)指向數(shù)組的指針,因此在函數(shù)內(nèi)部修改切片的元素,實(shí)際上是修改了這個(gè)內(nèi)部數(shù)組,從而影響了原始的切片。
使用指針實(shí)現(xiàn)引用傳遞
現(xiàn)在看看如何使用指針來(lái)實(shí)現(xiàn)類(lèi)似引用傳遞的效果,從而能夠在函數(shù)內(nèi)部修改基本類(lèi)型的值。簡(jiǎn)單示例代碼如下:
package main
import "fmt"
func modifyPointer(x *int) {
*x = 100
}
func main() {
original := 1
modifyPointer(&original)
fmt.Println(original) // 輸出 100,被修改
}
在這個(gè)例子中,傳遞了 original 變量的地址給 modifyPointer 函數(shù)。因?yàn)閭鬟f的是一個(gè)指向原始數(shù)據(jù)的指針的副本,所以當(dāng)在函數(shù)內(nèi)部通過(guò)這個(gè)指針修改數(shù)據(jù)時(shí),實(shí)際上修改的是原始變量的值。
結(jié)構(gòu)體的值傳遞
接下來(lái),通過(guò)一個(gè)結(jié)構(gòu)體的例子來(lái)說(shuō)明值傳遞的概念。簡(jiǎn)單示例代碼如下:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func modifyStruct(p Person) {
p.Name = "Alice"
p.Age = 30
}
func main() {
originalPerson := Person{Name: "Bob", Age: 25}
modifyStruct(originalPerson)
fmt.Println(originalPerson) // 輸出 {Bob 25},未被修改
}在上面的例子中,originalPerson 是一個(gè) Person 類(lèi)型的結(jié)構(gòu)體。當(dāng)被傳遞到 modifyStruct 函數(shù)時(shí),傳遞的是這個(gè)結(jié)構(gòu)體的副本。因此,函數(shù)內(nèi)部對(duì)結(jié)構(gòu)體的修改不會(huì)影響到原始的 originalPerson。
結(jié)構(gòu)體指針的傳遞
最后來(lái)看一個(gè)結(jié)構(gòu)體指針的例子,理解如何通過(guò)指針來(lái)修改結(jié)構(gòu)體的字段。簡(jiǎn)單示例代碼如下:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func modifyStructPointer(p *Person) {
p.Name = "路多辛"
p.Age = 20
}
func main() {
originalPerson := &Person{Name: "luduoxin", Age: 25}
modifyStructPointer(originalPerson)
fmt.Println(*originalPerson) // 輸出 {路多辛 20} ,被修改
}在這個(gè)例子中,傳遞了 originalPerson 的地址給 modifyStructPointer 函數(shù)。這次傳遞的是一個(gè)指向結(jié)構(gòu)體的指針的副本,所以在函數(shù)內(nèi)部對(duì)這個(gè)指針?biāo)赶虻慕Y(jié)構(gòu)體的修改,實(shí)際上改變了原始的`originalPerson`結(jié)構(gòu)體。
小結(jié)
Go 語(yǔ)言中的參數(shù)傳遞總是值傳遞,意味著傳遞的總是變量的副本,無(wú)論是基本數(shù)據(jù)類(lèi)型還是復(fù)合數(shù)據(jù)類(lèi)型。由于復(fù)合數(shù)據(jù)類(lèi)型(如切片、映射、通道、接口和指針)內(nèi)部包含的是對(duì)數(shù)據(jù)的引用,所以在函數(shù)內(nèi)部對(duì)這些參數(shù)的修改可能會(huì)影響到原始數(shù)據(jù)。理解這一點(diǎn)對(duì)于編寫(xiě)正確和高效的Go代碼至關(guān)重要。
另外即使是引用類(lèi)型,比如切片,當(dāng)長(zhǎng)度或容量(比如使用 append 函數(shù))發(fā)生變化了,可能會(huì)導(dǎo)致分配新的底層數(shù)組。這種情況下,原始切片不會(huì)指向新的數(shù)組,但是函數(shù)內(nèi)部的切片會(huì)。因此,如果想在函數(shù)內(nèi)部修改切片的長(zhǎng)度或容量并反映到外部,應(yīng)該傳遞一個(gè)指向切片的指針。
以上就是一文帶你深入了解Golang中的參數(shù)傳遞機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Go參數(shù)傳遞機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go標(biāo)準(zhǔn)容器之Ring的使用說(shuō)明
這篇文章主要介紹了Go標(biāo)準(zhǔn)容器之Ring的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
GO語(yǔ)言類(lèi)型轉(zhuǎn)換和類(lèi)型斷言實(shí)例分析
這篇文章主要介紹了GO語(yǔ)言類(lèi)型轉(zhuǎn)換和類(lèi)型斷言,以實(shí)例形式詳細(xì)分析了類(lèi)型轉(zhuǎn)換和類(lèi)型斷言的概念與使用技巧,需要的朋友可以參考下2015-01-01
Golang安裝和使用protocol-buffer流程介紹
這篇文章主要介紹了Golang安裝和使用protocol-buffer過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09
golang簡(jiǎn)易令牌桶算法實(shí)現(xiàn)代碼
這篇文章主要介紹了golang簡(jiǎn)易令牌桶算法實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
使用Golang的gomail庫(kù)實(shí)現(xiàn)郵件發(fā)送功能
本篇博客詳細(xì)介紹了如何使用Golang語(yǔ)言中的gomail庫(kù)來(lái)實(shí)現(xiàn)郵件發(fā)送的功能,首先,需要準(zhǔn)備工作,包括安裝Golang環(huán)境、gomail庫(kù),以及申請(qǐng)126郵箱的SMTP服務(wù)和獲取授權(quán)碼,其次,介紹了在config文件中配置SMTP服務(wù)器信息的步驟2024-10-10
Go使用Google?Gemini?Pro?API創(chuàng)建簡(jiǎn)單聊天機(jī)器人
這篇文章主要為大家介紹了Go使用Google?Gemini?Pro?API創(chuàng)建簡(jiǎn)單聊天機(jī)器人實(shí)現(xiàn)過(guò)程詳解,本文將通過(guò)最新的gemini?go?sdk來(lái)實(shí)現(xiàn)命令行聊天機(jī)器人2023-12-12
go語(yǔ)言for循環(huán)中嵌套defer的執(zhí)行順序
在Go語(yǔ)言中,defer語(yǔ)句用于延遲函數(shù)調(diào)用的執(zhí)行,本文主要介紹了go語(yǔ)言for循環(huán)中嵌套defer的執(zhí)行順序,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03

