golang函數(shù)傳遞slice是值傳遞還是引用傳遞?
先說(shuō)結(jié)論:go中所有的函數(shù)傳參都為值傳遞
在 Go 語(yǔ)言中,切片(slice)是引用類型,因此在函數(shù)調(diào)用中傳遞切片時(shí),實(shí)際上是傳遞了切片的引用(地址),而不是切片的副本。這意味著對(duì)傳遞的切片進(jìn)行修改會(huì)影響原始切片的內(nèi)容。
當(dāng)將切片作為函數(shù)參數(shù)傳遞時(shí),函數(shù)接收的是切片的引用。這使得函數(shù)可以訪問(wèn)和修改原始切片的元素,而無(wú)需進(jìn)行顯式的指針操作。
下面是一個(gè)示例代碼來(lái)說(shuō)明切片是如何通過(guò)引用傳遞的:
package main
import "fmt"
func modifySlice(s []int) {
s[0] = 100
s = append(s, 200)
}
func main() {
slice := []int{1, 2, 3}
fmt.Println("Before:", slice) // 輸出: [1 2 3]
modifySlice(slice)
fmt.Println("After:", slice) // 輸出: [100 2 3],這里并沒(méi)有append的200!!
}
在上面的示例中,我們定義了一個(gè) modifySlice 函數(shù),它接收一個(gè)切片作為參數(shù)。在函數(shù)內(nèi)部,我們將切片的第一個(gè)元素修改為 100,并向切片追加一個(gè)新的元素 200。
在 main 函數(shù)中,我們創(chuàng)建了一個(gè)切片 slice,并將其傳遞給 modifySlice 函數(shù)。在函數(shù)內(nèi)部,我們修改了切片的第一個(gè)元素,并追加了一個(gè)新的元素。然后,我們?cè)?main 函數(shù)中打印輸出原始切片 slice,可以看到修改已經(jīng)影響了原始切片的內(nèi)容。
這表明切片在函數(shù)調(diào)用中是貌似通過(guò)引用傳遞的,因?yàn)楹瘮?shù)對(duì)切片的修改會(huì)影響原始切片。這種行為與值類型(如整數(shù)、字符串等)的傳遞方式不同,值類型在函數(shù)調(diào)用中是通過(guò)值傳遞的,函數(shù)對(duì)值類型的修改不會(huì)影響原始值。
既然是引用傳遞,為啥外層slice沒(méi)有最后一個(gè)元素200呢?
slice包含三個(gè)成員:指向底層數(shù)組的指針,slice的長(zhǎng)度,長(zhǎng)度的容量
go中所有的函數(shù)傳參都為值傳遞,當(dāng)slice作為入?yún)ⅲ⑶覍?duì)slice進(jìn)行修改時(shí),由于指向數(shù)組的指針指向同一個(gè)地址,對(duì)數(shù)組的修改會(huì)同步到函數(shù)外部的切片上;但是slice的長(zhǎng)度和容量是兩個(gè)值變量,函數(shù)內(nèi)部對(duì)他們的修改無(wú)法傳遞到函數(shù)外,所以會(huì)造成函數(shù)內(nèi)對(duì)slice進(jìn)行append操作,底層數(shù)組發(fā)生了變化,但是函數(shù)外部slice因無(wú)法感知內(nèi)部切片長(zhǎng)度的變化,而沒(méi)有修改的情況;另外,當(dāng)slice的長(zhǎng)度要超出slice的容量時(shí),slice會(huì)進(jìn)行擴(kuò)容:申請(qǐng)一塊新的地址作為底層數(shù)組,將舊的數(shù)組上的值拷貝過(guò)來(lái),擴(kuò)容完成后,函數(shù)內(nèi)切片指向的數(shù)組與函數(shù)外切片指向的數(shù)組就不再是同一個(gè)地址,所有的修改都不會(huì)生效。
點(diǎn)擊查看代碼
func main() {
slice := []int{1, 2, 3}
fmt.Println("Before:", slice) // 輸出: [1 2 3]
modifySlice(slice)
fmt.Println("After:", slice) // 輸出: [1 2 3]
}
func modifySlice(s []int) {
s = append(s, 4) //長(zhǎng)度要超出slice的容量時(shí),slice會(huì)進(jìn)行擴(kuò)容:申請(qǐng)一塊新的地址作為底層數(shù)組,將舊的數(shù)組上的值拷貝過(guò)來(lái),擴(kuò)容完成后,函數(shù)內(nèi)切片指向的數(shù)組與函數(shù)外切片指向的數(shù)組就不再是同一個(gè)地址,后面所有的修改都不會(huì)生效
s[0] = 100
fmt.Println("temp:", s) // 輸出: [100 2 3 4]
}
slice := []int{1, 2, 3}
fmt.Println("Before:", slice) // 輸出: [1 2 3]
modifySlice(slice)
fmt.Println("After:", slice) // 輸出: [1 2 3]
}
func modifySlice(s []int) {
s = append(s, 4) //長(zhǎng)度要超出slice的容量時(shí),slice會(huì)進(jìn)行擴(kuò)容:申請(qǐng)一塊新的地址作為底層數(shù)組,將舊的數(shù)組上的值拷貝過(guò)來(lái),擴(kuò)容完成后,函數(shù)內(nèi)切片指向的數(shù)組與函數(shù)外切片指向的數(shù)組就不再是同一個(gè)地址,后面所有的修改都不會(huì)生效
s[0] = 100
fmt.Println("temp:", s) // 輸出: [100 2 3 4]
}
解決方案:將切片的指針作為參數(shù)傳遞進(jìn)去
func append_test (arr []int) {
arr = append(arr, 555,555)
(arr)[0] = 55 //注意這里的寫法 (arr)[] //arr[] 會(huì)被編譯器理解為*(arr[index])
}

浙公網(wǎng)安備 33010602011771號(hào)