Go 語言陷阱 - 陣列和切片
阿新 • • 發佈:2021-10-24
https://geektutu.com/post/hpg-gotchas-array-slice.html
語言陷阱系列文章連結:
- Go 語言陷阱 - 陣列和切片(Dec 7, 2020)
原始碼/資料集已上傳到Github - high-performance-go
1 第一個陷阱
1.1 下面程式的輸出是
1
|
func foo(a [2]int) {
|
1.2 答案
正確的輸出是[1 2]
,陣列a
沒有發生改變。
- 在 Go 語言中,陣列是一種值型別,而且不同長度的陣列屬於不同的型別。例如
[2]int
[20]int
屬於不同的型別。 - 當值型別作為引數傳遞時,引數是該值的一個拷貝,因此更改拷貝的值並不會影響原值。
我們在切片(slice)效能及陷阱這篇文章中也提到了,為了避免陣列的拷貝,提高效能,建議傳遞陣列的指標作為引數,或者使用切片代替陣列。
1.3 更多
如果將上述程式替換為:
1
|
func foo(a *[2]int) {
|
或
1
|
func foo(a []int) {
|
輸出將會變成[200 2]
。
在切片(slice)效能及陷阱這篇文章中,我們也提到了切片由三個值構成:
*ptr
指向底層陣列的指標len
長度cap
容量
因此,將切片作為引數時,拷貝了一個新切片,即拷貝了構成切片的三個值,包括底層陣列的指標。對切片中某個元素的修改,實際上是修改了底層陣列中的值,因此原切片也發生了改變。
2 第二個陷阱
2.1 下面程式的輸出是
1
|
func foo(a []int) {
|
2.2 答案
輸出仍是[1 2]
,切片a
沒有發生改變。
傳參時拷貝了新的切片,因此當新切片的長度發生改變時,原切片並不會發生改變。而且在函式foo
中,新切片a
增加了 8 個元素,原切片對應的底層陣列不夠放置這 8 個元素,因此申請了新的空間來放置擴充後的底層陣列。這個時候新切片和原切片指向的底層陣列就不是同一個了。因此,對新切片第 0 個元素的修改,並不會影響原切片的第 0 個元素。
如果如果希望foo
函式的操作能夠影響原切片呢?
兩種方式:
- 設定返回值,將新切片返回並賦值給
main
函式中的變數a
。 - 切片也使用指標方式傳參。
1
|
func foo(a []int) []int {
|
或
1
|
func foo(a *[]int) {
|
上述兩個程式的輸出均為:
1
|
[200 2 1 2 3 4 5 6 7 8]
|
從可讀性上來說,更推薦第一種方式