數組切片slice這個東西看起來很美好,真正用起來會發現有諸多的不爽。
第一,數組、數組切片混淆不清,使用方式完全一樣,有時候一些特性又完全不一樣,搞不清原理很容易誤使用。
第二,數組切片的append操作,每次對slice append操作,都返回一個新的slice的引用,對slice的引用沒法保持,這樣在函數傳遞slice的情況下append,在調用函數的上下文中看不到slice append的效果。如果想要這種方式湊效,不得不另辟蹊徑。本文主要說一下如何解決這個窘境的方法。
函數傳遞slice存在什么問題?
func sliceModify(slice []int) { // slice[0] = 88 slice = append(slice, 6) } func main() { slice := []int{1, 2, 3, 4, 5} sliceModify(slice) fmt.Println(slice) }
輸出:
[1 2 3 4 5]
問題所在:
雖然說數組切片在函數傳遞時是按照引用的語義傳遞的,比如說在sliceModify函數里面slice[0] = 88,在方法調用的上下文中,調用函數對slice引用的改表是看得見的。
但是在對slice進行append操作的時候,我們驚奇的發現,這次又不管用了。原因就是append操作會返回這個擴展了的slice的引用,必須讓原引用重新賦值為新slice的引用,說白了就是,傳遞過來的這個指針原來指了內存中的A區域,A區域是原數組的真正所在。經過一次 append之后,要把這個指針改為指向B,B對應append后新的slice的引用。但是方法調用的上下文里的slice指針還是指向了老的A內存區域。
這個邏輯實在有些奇葩,這里我不得不再次吐槽append的設計。有人說這個問題好解決啊,只需要在sliceModify函數的返回值中把append后新的slice引用返回就好了。這樣做當然是可以滴,但是像遞歸調用的函數就不好解決了。
下面就說一下這個問題的解決辦法,方法也很簡單,就是傳遞指針的指針。雖然有些繞,但是總算把問題解決了。當然也有其他的辦法,比如按照java等語言的方式,自己實現一個ArrayList,在對可變數組擴展的時候,千萬表改變引用了。
func sliceModify(slice *[]int) { *slice = append(*slice, 6) } func main() { slice := []int{1, 2, 3, 4, 5} sliceModify(&slice) fmt.Println(slice) }
這次就可以輸出預期的結果了:
[1 2 3 4 5 6]
遞歸調用的例子:
func insertTo10(arr *[]int) { length := len(*arr) if length == 10 { return } *arr = append(*arr, length) insertTo10(arr) } func main() { arr10 := []int{} insertTo10(&arr10) fmt.Println(arr10) }
還可以訪問我樹莓派上搭的博客地址: