值类型赋值复制整块数据,指针赋值仅复制地址;函数传参均为值传递,但传值类型拷贝全部内容,传指针仅拷贝地址;方法接收者选值或指针取决于是否需修改字段,统一使用指针接收者可避免接口实现问题。

值类型赋值=复制整块数据
当你写 a := b,且 b 是 int、string 或小 struct 时,Go 真的把所有字节都拷贝了一份。两个变量完全独立,改一个不影响另一个。
- 常见错误现象:
person := User{Name: "Alice"},再copy := person,然后copy.Name = "Bob",结果person.Name还是"Alice" - 适用场景:只读操作、函数内临时计算、需要隔离修改的小对象(比如配置项快照)
- 性能影响:结构体字段少、无大数组/切片时开销极小;但若含
[1024 * 1024]byte,一次赋值就拷贝 1MB
指针赋值=复制地址,共享同一块内存
p := &x 得到的是地址值,q := p 只是把那个地址(比如 0xc00001a240)又存了一份——两个指针指向同一片内存。
- 常见错误现象:
ptr1 := &user,ptr2 := ptr1,然后*ptr2 = User{Name: "Charlie"},*ptr1立刻变成"Charlie";如果忘了判空,*nilPtr直接 panic - 适用场景:需要跨函数/方法修改原值、避免大结构体拷贝、实现共享状态(如缓存、连接池句柄)
- 兼容性注意:
*T类型的方法集包含T和*T的所有方法,但T类型的方法集**只含T上定义的方法**;混用会导致接口实现不一致
函数传参时,值和指针的行为差异最明显
Go 所有参数都是值传递,但“值”的内容不同:传 User 是传整个结构体副本;传 *User 是传一个 8 字节的地址副本。
- 容易踩的坑:
func update(u User) { u.Age = 30 }永远改不了外面的user;必须写成func update(u *User) { u.Age = 30 }并调用update(&user) - 性能判断依据:结构体大小超过 8–12 字节、含引用类型字段(如
map、slice)、或未来可能加修改方法 → 优先传*T - 切片/map/channel 是特例:它们本身是值类型(header 结构),但 header 里含指针;所以能改元素,不能改长度或底层数组 —— 别误以为“它们是引用类型就不用取地址”
方法接收者选值还是指针?看要不要改字段
接收者是 (u User) 还是 (u *User),决定了这个方法能不能真正改变调用者的字段。
立即学习“go语言免费学习笔记(深入)”;
- 必须用指针接收者的情况:
func (u *User) SetName(name string) { u.Name = name };否则赋值只作用于副本 - 值接收者更安全的场景:
func (u User) FullName() string { return u.Name + " " + u.Surname },不会意外污染原数据 - 统一性建议:只要有一个方法用了指针接收者,整个类型最好全用
*User接收者,否则User值变量无法满足含指针方法的接口
最常被忽略的一点:指针零值是 nil,而值类型零值是确定的(0、""、false)。解引用前不检查 if p != nil,程序会在运行时崩溃,而且这种 panic 往往发生在深层调用里,排查成本高。










