Go函数默认值传递,修改原变量需传指针;仅可寻址变量能取地址;指针参数名和函数名应体现修改意图并检查nil;结构体优先传指针;切片/map/channel本身引用语义,但替换底层数组需指针。

在 Go 语言中,函数默认按值传递参数,若想在函数内部修改调用方的变量值,必须传入该变量的指针。这不是“技巧”,而是 Go 的核心设计原则——明确性与可控性。关键在于理解何时取地址、何时解引用,以及避免常见陷阱(比如对 nil 指针解引用或误传值类型地址)。
传指针前先确认变量可寻址
只有可寻址的变量才能取地址(即能写 &x)。常量、字面量、函数返回值(非地址)、map 中的元素(除非是结构体字段且 map 已初始化)、接口值本身等都不可寻址。
- ✅ 正确:定义变量后传其地址
- ❌ 错误:
modify(&42)(字面量不可取地址)或modify(&m["key"])(map 元素不可寻址,除非是&structField且 struct 在 map 中)
函数签名要清晰表达“会修改输入”
接收指针的参数名和函数名应体现意图。例如用 incrementCounter 比 doSomething 更明确;参数可命名为 count *int 而非 v *int。
同时,务必检查指针是否为 nil,尤其当指针可能来自外部调用时:
立即学习“go语言免费学习笔记(深入)”;
func incrementCounter(count *int) {
if count == nil {
return // 或 panic("count is nil"),依业务而定
}
*count++
}
结构体指针是高频场景,优先使用
对于结构体,传指针既避免复制开销,又自然支持字段修改。方法接收者也常用指针,以保证修改生效:
type User struct {
Name string
Age int
}
func (u *User) GrowOld() {
u.Age++
}
func main() {
u := User{Name: "Alice", Age: 30}
u.GrowOld() // ✅ 方法内修改了原始 u 的 Age
fmt.Println(u.Age) // 输出 31
}
注意:如果方法接收者是 func (u User)(值接收者),u.Age++ 只修改副本,原结构体不变。
切片、map、channel 本身含引用语义,通常不需额外传指针
切片底层指向数组,修改元素(如 s[0] = 1)会影响原切片;map 和 channel 同理。但若需替换整个底层数组(如扩容后重新赋值给原变量),仍需指针:
func appendAndReplace(s *[]int, x int) {
*s = append(*s, x)
}
func main() {
data := []int{1, 2}
appendAndReplace(&data, 3)
fmt.Println(data) // [1 2 3]
}
这里 &data 是因为 append 可能分配新底层数组,必须通过指针把新地址写回原变量。










