【切片】基礎不扎實引發(fā)的問題

本次文章主要是來聊聊關于切片傳值需要注意的問題,如果不小心,則很容易引發(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)幾點關于切片的注意事項

  1. Golang 中的函數(shù)參數(shù),都是傳值,不是傳地址
  1. 對于切片自身的底層數(shù)據(jù)結構,我們可以通過索引的方式拿到底層數(shù)組的地址,并修改其地址上的值,例如 sli[2] = "hello",這是可以直接修改
  1. 如果傳入的切片,期望實參也能夠被改變的話,那么就需要想辦法修改切片的底層數(shù)組

    1. 通過傳切片的地址,也就是傳指針的方式
    2. 在函數(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),更應是開放的。擁抱變化,向陽而生,努力向前行。

我是阿兵云原生,歡迎點贊關注收藏,下次見~

文中提到的技術點,感興趣的可以查看這些文章:

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容