先学值类型再学指针是最顺路径;值类型赋值即复制、行为直观,是建立直觉基础;指针仅在需“改原值”或“省拷贝”时引入,如方法修改结构体字段或大结构体传参。

先学值类型,再学指针——这是最顺、最不绕弯的路径。Go 的指针不是必须一开始就啃的硬骨头,而是为解决“改原值”和“省拷贝”这两个具体问题才引入的工具。
为什么不能一上来就钻指针?
因为新手常把 & 和 * 当成魔法符号,死记硬背却不知道“改没改到原变量”。而值类型行为直观:赋值即复制,函数里改它,外面纹丝不动。这种可预测性是建立直觉的基础。
- 用
int、string、struct{ Name string; Age int }写几个函数,观察传参后原变量是否变化 - 亲手写一个
func double(x int) int和func doublePtr(x *int)对比,看哪个能真正把外面的数翻倍 - 跳过“指针运算”“多级指针”等 C 风格内容——Go 不支持指针算术,也不需要
什么时候该切到指针学习?
当你遇到这两个信号之一,就是该学指针的时候了:
-
方法要改结构体字段:比如
func (p *User) SetEmail(e string),不用指针接收者,p.Email = e就只是改副本 -
结构体变大了:加了
[]byte、map[string]int或十几个字段后,func process(u User)开始明显变慢——这时用*User传参,避免每次调用都复制几百字节
此时再回看 &user 是取地址、*p 是解引用,就不再是语法,而是“我需要它来达成某个目的”的自然选择。
立即学习“go语言免费学习笔记(深入)”;
容易踩的坑:逃逸分析和 nil 指针
初学者常以为“用了指针就一定在堆上”,其实 Go 编译器会根据变量是否逃逸决定分配位置。比如函数返回局部结构体的地址:
func newUser() *User {
u := User{Name: "Alice"}
return &u // u 逃逸,分配在堆
} 这没问题;但若误判为“所有指针都堆分配”,反而可能因过度使用指针增加 GC 压力。
- 别盲目给小结构体(如
type Point struct{ X, Y int })加指针——值传递更高效也更安全 - 返回指针前,确认不会返回
nil:比如func parseConfig() *Config若配置缺失应返回错误,而不是让调用方处理if c == nil - 接口实现时注意:如果结构体有
sync.Mutex字段,必须用指针接收者,否则锁会被复制失效
真正的难点不在语法,而在于判断“这个数据,我到底想让它被共享修改,还是隔离保护”。想清楚这一点,值和指针就只是同一枚硬币的两面,而不是两套语言。









