**为啥 Go 还保留指针?
我昨天晚上十一点多在工位上改 bug 的时候,小李凑过来一句:“东哥我操我这个 struct 传来传去怎么每次改都不生效啊?” 我一看,他每次都 copy 结构体…你让它生效它也生效不了啊对吧…
我就顺嘴嘟囔了一句:“Go 要是没指针,你这 bug 得写三倍。”
你别看指针听起来吓人,其实在 Go 里它就是——方便、省内存、提高性能、减少数据拷贝… 而且 Go 把它“阉割”得挺狠,根本不像 C 那种能把人吓到怀疑人生的级别。
1. 不拷贝大对象的时候特别香
我前两天写个接口返回 5000 条数据,结构体可大了,要是每层函数都 copy 一遍,那 CPU 得哭。
就类似这种:
type User struct {
Name string
Email string
Scores []int
}
func load(u *User) {
u.Scores = append(u.Scores, 100)
}
我那天跟小王演示的时候,他还来一句:“卧槽这么小一个 * 就省这么多事?”
是啊兄弟,这就是 Go 指针留下来的最主要目的:减少拷贝,性能不至于被折腾死。
2. 需要修改调用者数据的时候,你别绕弯子
Go 的值传递你们知道吧?所有东西默认都是 copy。 有时候你就是得“改原来的”,比如 map 对象里的东西、网络请求解析出来的内容,还比如我们公司的那个批量任务调度里,对象要在中途不断patch。
不用指针你只能一直 return return return,return 到你怀疑人生。
我上周半夜改一个小功能,就是因为多传了几次参数…人都快没了。
用指针就很直接:
func fix(v *Config) {
v.Enabled = true
}
不用 * 的话…反正你知道会多麻烦。
3. Go 很狠,把危险的部分都去掉了
我记得第一次教新人写 Go 时,那哥们脸都白了:“东哥…Go 也有指针啊?那不就像 C 那样随便搞一下就挂了?”
我直接拍桌子:“兄弟你太高看 Go 了,它根本没给你机会作死。”
Go 的指针不能:
-
不能做指针运算 -
不能 malloc/free -
不能跳来跳去玩内存地址 -
不能自己乱搞生命周期
你就当它是一个“引用”,只是写法像指针罢了。
它安全得很,你顶多把传值/传指针弄错,不会弄出段错误那种级别的血案(当然 race condition 你自己写错那就是你的事了)。
4. 内存逃逸 + GC 机制让指针成为必须品
我在公司楼下抽烟时,小李突然问我: “哥我这玩意儿怎么一打日志就逃逸到堆上了?”
我说你看吧:
func build() *User {
u := User{Name: "dong"}
return &u
}
这东西 Go 编译器分析到“它要在函数外活着”,自动帮你丢堆上去。 你根本管不着。
有指针 GC 才能管理对象生命周期,不然 Go 就不是 Go 了。
5. 有些地方不用指针压根没法写
像 channel 传大对象、JSON 反序列化、数据库扫描结构体… 这些场景不用指针根本没法搞。
比如数据库扫数据:
db.QueryRow(`select name from user`).Scan(&u.Name)
不用 &? 抱歉你一个值传递肯定无法塞回去。
那 Go 指针到底坑不坑?
坑是真有,比如:
-
nil 指针忘记判断 -
struct 传值/传指针弄混 -
切片底层指针共享导致的“改一处动全家”
但是说实话…这些坑跟 C 那种“踩了直接歇菜”的坑完全不一个级别。
Go 把危险部分都封死了,只保留“高性能 + 可修改数据”这两块。
指针不是因为“Go 想复杂”,而是现实摆在那——
你要性能,又要易用,又要安全,没有指针根本做不到。
不过 Go 做得很极限,它只留了“能提升性能”的部分,把“能让工程师互相甩锅”的部分都删了。
所以别怕它。 你多用几次就会发现:这是你见过最温柔的指针。
行,我得去接个电话,你们要是想让我顺便讲讲“什么时候该用指针什么时候不用”,你吱一声。