Go中修改原变量必须传指针:函数参数是值传递,切片/map仅复制头信息;结构体方法改字段需指针接收者;解引用前须判空;切片append可能改变底层数组地址。

Go里传指针才能改原变量值
Go函数参数是值传递,哪怕传的是切片或map,底层也是复制头信息。想在函数里真正修改调用方的变量,必须传它的地址——也就是*T类型。常见误区是以为传struct或int进去就能改,结果发现原值纹丝不动。
实操建议:
- 定义函数参数为
*int、*string或*MyStruct,而不是int、string - 调用时用
&x取地址,别直接传x - 函数内部用
*p = new_value解引用赋值,不是p = &new_value
错误示例:func badInc(x int) { x++ } —— 这只改了副本,原变量不受影响。
修改结构体字段必须用指针接收者
给结构体方法加功能时,如果方法要改结构体字段(比如user.SetAge(25)),接收者必须是*User。用User值接收者的话,方法操作的是结构体副本,字段修改不会回写到原实例。
立即学习“go语言免费学习笔记(深入)”;
实操建议:
- 判断标准很简单:这个方法会不会改变接收者的任何字段?会 → 用
func (u *User) SetName(n string) - 即使结构体很小(比如只有两个
int),也别图省事用值接收者改字段,语义上就是错的 - 混用值接收者和指针接收者会导致方法集不一致:
*T能调所有方法,T只能调值接收者方法
典型报错:cannot call pointer method on u 或 cannot take the address of u,往往是因为你用u := User{}声明后直接调用了指针接收者方法。
nil指针解引用会panic,务必先判空
Go里对nil指针做*p操作会立即触发panic: runtime error: invalid memory address or nil pointer dereference。这比空指针异常更“干脆”,但也更易漏掉检查。
实操建议:
- 所有解引用前加
if p != nil,尤其是函数参数为*T时 - 不要依赖“反正这里不会是nil”——接口传参、JSON反序列化失败、map查找未命中都可能产出
nil指针 - 初始化结构体字段为指针时,显式赋
new(T)或&T{},避免零值nil
示例:if user != nil && user.Profile != nil { name = user.Profile.Name } —— 少一个!= nil就可能崩。
切片和map本身是指针包装,但底层数组地址仍需小心
切片变量包含指向底层数组的指针,所以append可能造成底层数组扩容,导致原切片和新切片不再共享内存。这时候即使你传的是*[]int,也可能改不到预期位置。
实操建议:
- 真要保证修改生效,要么传
*[]T并用*s = append(*s, x),要么直接返回新切片让调用方重赋值 - map作为参数传入函数后,增删改key都是生效的(因为map header含指针),但
map = make(map[string]int这种重新赋值不影响原变量 - 别对
nil切片或map做append或delete前忘了初始化:if s == nil { s = []int{} }
最容易被忽略的是:切片长度变化不等于内容变化,len(s)变了不代表s[0]能安全读 —— 可能刚被append扩容甩到另一块内存去了。










