本次文章主要是來聊聊關于切片傳值需要注意的問題,如果不小心,則很容易引發(fā)線上問題,如果不夠理解,可能會出現(xiàn)奇奇怪怪的現(xiàn)象
問題情況:
小 A 負責一個模塊功能的實現(xiàn),在調(diào)試代碼的時候可能不仔細,部署到線上環(huán)境時發(fā)現(xiàn)在現(xiàn)有策略列表上追加新的策略時,總是無法生效,這是為什么呢?
追查代碼后發(fā)現(xiàn)問題出在關于切片的使用上出了認知偏差,小 A 認為 golang 中,傳切片就是傳引用,因此寫出了這樣的代碼片段
func xxxFunc(sli []int ,newSli []int) {
// ... 省略部分代碼
sli = append(sli, newSli...)
// ... 省略部分代碼
return
}
想表達的意思是:
傳入的 sli 切片屬于舊切片,期望在 sli 切片上追加 newSli 中的元素,最終期望得到的 sli 里面是包含 newSli 元素的
然而,對于 Golang 中切片 slice 有一定了解的 xdm 就很清楚,這樣寫其實并沒有什么實際作用,在 Golang 中傳參都是傳值而不是傳地址
因此此處傳入的 sli 切片,也僅僅是一個拷貝而已,在 xxxFunc 函數(shù)中的 sli 切片被修改了,實際上是不會影響函數(shù)外部的 sli 的
那么對于切片此處做幾個闡述
首先強調(diào)幾點關于切片的注意事項
- Golang 中的函數(shù)參數(shù),都是傳值,不是傳地址
- 對于切片自身的底層數(shù)據(jù)結構,我們可以通過索引的方式拿到底層數(shù)組的地址,并修改其地址上的值,例如
sli[2] = "hello",這是可以直接修改
-
如果傳入的切片,期望實參也能夠被改變的話,那么就需要想辦法修改切片的底層數(shù)組
- 通過傳切片的地址,也就是傳指針的方式
- 在函數(shù)中,去索引切片的底層數(shù)組地址,進行修改數(shù)據(jù)
案例 1 遍歷的時候修改
通過 value 修改切片值 - 不靠譜
我們給出一個切片 var mySlice = []int{7, 8, 9} ,并編寫如下幾個函數(shù)來查看是否會對原有切片數(shù)據(jù)有影響
func main() {
log.SetFlags(log.Lshortfile)
var mySlice = []int{7, 8, 9}
log.Printf("mySlice == %+v", mySlice)
log.Println("---------------------------------------------")
mySliceDemo := testDemo(mySlice)
log.Printf("mySlice == %+v ,mySliceDemo == %+v", mySlice, mySliceDemo)
log.Printf("mySlice == %p ,mySliceDemo == %p", &mySlice, &mySliceDemo)
}
func testDemo(sli []int) []int {
for _, value := range sli {
value *= 2
}
return sli
}
給 testDemo 傳入 mySlice 切片,在函數(shù)內(nèi)部通過 for...range 的方式去修改切片內(nèi)元素的值,然而代碼中的 value 仍然是一個拷貝,他并不會真的對外部的 mySlice 有任何影響,結果自然是這樣的
[圖片上傳失敗...(image-61c9ac-1695996180623)]
可以通過修改切片索引上的值
當然如果我們這樣寫,去找到索引對應的底層數(shù)組的地址,再修改其地址上的值,是可行的
func testDemo2(sli []int) []int {
for index, _ := range sli {
sli[index] *= 2
}
return sli
}
[圖片上傳失敗...(image-b152e0-1695996180623)]
自然通過指針的方式仍然可以
傳入的這個指針,實際上也是一個拷貝,只不過拷貝的是這個指針,也就是指針自身的地址不一樣,但是他們指向的底層數(shù)組是一樣的,因此可以直接修改
這種修改的方式,也是去修改地址上的值,因此有效
func testDemo3(sli *[]int) {
for index, _ := range *sli {
(*sli)[index] *= 2
}
return
}
結果自然 ok,原有 mySlice的地址也是沒有發(fā)生變化的,只是值發(fā)生了變化
[圖片上傳失敗...(image-813222-1695996180623)]
案例 2 使用 append 會有什么不同
那么如果是在子函數(shù)里面使用 append 追加數(shù)據(jù),是否會有不同的效果?
func appendDemo(sli []int) []int {
sli = append(sli, 999)
return sli
}
[圖片上傳失敗...(image-e3455d-1695996180623)]
實際上,此處傳入的仍然是 mySlice 的拷貝,appendDemo 中使用 append,也是基于拷貝后的值來進行數(shù)據(jù)追加
哪怕是遇到切片擴容的情況,也僅僅是對于函數(shù)內(nèi)的拷貝副本來進行擴容和變化,例如這樣
func appendDemo3(sli []int)[]int{
sli = append(sli, []int{3,4}...)
return sli
}
[圖片上傳失敗...(image-aab1a5-1695996180623)]
傳入切片的地址
在使用 append 的情況, 向函數(shù)參數(shù)中傳入切片的指針,此處對于函數(shù)來說,仍然是一個副本,只不過這個副本是指針,指向的底層數(shù)組仍然是和 mySlice 是一樣的,因此可以通過這個拷貝的指針去修改實際底層數(shù)組的值
func appendDemo2(sli *[]int){
*sli = append(*sli, []int{1000,10001}...)
return
}
[圖片上傳失敗...(image-ff65bd-1695996180623)]
可以看到使用指針的方式,處理起來還是妥妥的,在 appendDemo2 中實際修改了 mySlice 的值,且也是我們所期望的
至此,對于文章開頭問題的解決方式,xdm 心中都有數(shù)了吧,那就不能再犯了吧,希望能夠給你帶來幫助
感謝閱讀,歡迎交流,點個贊,關注一波 再走吧
歡迎點贊,關注,收藏
朋友們,你的支持和鼓勵,是我堅持分享,提高質(zhì)量的動力
[圖片上傳失敗...(image-b4be05-1695996180623)]
技術是開放的,我們的心態(tài),更應是開放的。擁抱變化,向陽而生,努力向前行。
我是阿兵云原生,歡迎點贊關注收藏,下次見~
文中提到的技術點,感興趣的可以查看這些文章: