在 Go 語言中,所有函數的參數傳遞都是值傳遞(Pass by Value)。當你將一個變量作為參數傳遞給函數時,實際上傳遞的是該變量的副本,而不是變量本身。理解這一點對于避免常見的編程錯誤至關重要。根據不同的類型,以下作簡要的區分和介紹
1. 基本類型(值類型)
例如:int, float, bool, string, struct 等類型。
函數內部操作的是原始值的副本,修改不會影響原變量。
func modifyValue(n int) {n = 100 // 修改的是副本
}func main() {x := 10modifyValue(x)fmt.Println(x) // 輸出 10,原值未變
}
2. 指針類型(T)
傳遞指針時,復制的是指針的副本,但副本和原指針指向同一塊內存。
函數內通過指針修改的是原變量的值。
func modifyPointer(n *int) {*n = 100 // 通過指針修改原值
}func main() {x := 10modifyPointer(&x)fmt.Println(x) // 輸出 100
}
Tips:雖然指針是值傳遞,但通過間接訪問可以修改原數據。
3. 引用類型(Slice、Map、Channel)
這些類型內部包含指向底層數據結構的指針,傳遞時復制的是它們的“描述符”(如切片的指針、長度、容量),而不是底層數據本身。
函數內對元素的修改會影響原變量,但修改描述符(如擴容切片)不會影響原變量。
func modifySlice(s []int) {s[0] = 100 // 修改底層數組,影響原切片s = append(s, 20 0) // 修改副本的描述符,原切片不受影響
}func main() {s := []int{1, 2, 3}modifySlice(s)fmt.Println(s) // 輸出 [100 2 3]
}
注意誤區:如果進行append操作導致擴容,如切片這時可能會新分配數組,這時候原切片和參數副本的行為就會不同,會導致修改行為不符合預期。但函數內對 Map 的修改始終影響原數據,因為 Map 本身就是引用類型。Channel一般不存在擴容場景。
關鍵總結
-
值傳遞的本質:所有參數傳遞都是復制值的副本。
-
指針和引用類型的表現:雖然傳遞的是副本,但通過指針或內部指針間接訪問數據時,可以修改原數據。
-
性能考慮:
- 大型結構體使用指針傳遞避免復制開銷。
- 切片、Map、Channel 本身是輕量級結構,直接傳遞即可。