終極真相:Go 中的參數傳遞

在 Go 社區常能聽到 “按值傳遞”“按引用傳遞” 兩種說法:

然而,上述分類容易造成誤解。在 Go 語言中,一切函數實參都以  的形式被複制傳遞。區別僅在於:

  1. 複製的是 “完整數據”(整數、數組等);

  2. 複製的是 “描述符” 或指針(切片、映射、字符串等)。

理解這一點後,再看各類示例便能水落石出。

純值類型:數組、結構體等

func do(b [3]int) int {
    b[0] = 0         // 只修改副本
    return b[1]
}

func main() {
    a := [3]int{1, 2, 3}
    v := do(a)       // 傳參時完整複製
    fmt.Println(a, v) // [1 2 3] 2
}

描述符類型:映射(map)

func do(m map[int]int) {
    m[3] = 1
    m[4] = 3
}

func main() {
    m := map[int]int{4: 2}
    do(m)                      // 僅複製 map 頭部(指針)
    fmt.Println(m)             // map[3:1 4:3]
}

切片:三字節描述符的特殊性

func do(s []int) int {
    s = append(s, 4) // 可能觸發重新分配
    s[0] = 0
    return s[1]
}

func main() {
    a := []int{1, 2, 3}         // len=3 cap=3
    v := do(a)                  // 複製切片頭部
    fmt.Println(a, v)           // [1 2 3] 2
}

若確需影響調用方切片的長度 / 容量,可顯式傳入 *[]T

func do(s *[]int) {
    *s = append(*s, 4) // 直接改寫調用者變量
    (*s)[0] = 0
}

參數永遠 “不是別名”

Go 永遠複製實參,將其存入被調函數棧幀;函數內變量 絕不是調用方變量的別名
想要修改調用者的數據:就傳遞能 “間接定位” 到它的東西(指針或接口值的內部指針)。

Nb0fYG

結論與建議

  1. 牢記:Go 只有按值傳遞。不要再談 “按引用”,最多是 “值裏裝着指針”。

  2. 修改調用者數據的途徑只有兩種:

    • 傳遞指針(*T**T……);

    • 傳遞內部含指針的描述符(切片、map、通道)並操作其指向的共享數據。

  3. 重新分配(appendmake 等)僅改變局部副本頭部,不會回寫調用方。

  4. 若你需要讓函數 “生長” 切片或重新綁定 map,使用指針語義:func f(s *[]T) 或 func g(m *map[K]V)

正確理解參數傳遞語義,可避免誤判內存行爲、消除隱蔽 bug,使代碼行爲更加可預測。

參考

參考資料

[1]  Go Class: 04 Strings: https://www.youtube.com/watch?v=nxWqANttAdA&list=PLoILbKo9rG3skRCj37Kn5Zj803hhiuRK6&index=5

[2]  Go Class: 08 Functions, Parameters & Defer: https://www.youtube.com/watch?v=wj0hUjRHkPs&list=PLoILbKo9rG3skRCj37Kn5Zj803hhiuRK6&index=9&t=9s

[3]  Go Slices: usage and internals: https://go.dev/blog/slices-intro#:~:text=Go%E2%80%99s%20arrays%20are%20values,think%20about%20arrays%20is%20as

[4]  Demystifying function parameters in Go: https://www.alexedwards.net/blog/demystifying-function-parameters-in-go

[5]  Go Data Structures: https://research.swtch.com/godata#:~:text=A%20,a%20potentially%20different%20pointer%20and

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/IhZRi_1DvgRGnzs6Qv-18A