学习记录
Go 指针详解:地址、解引用与常见误区
从变量地址、解引用、参数传递到常见误区,系统梳理 Go 指针到底解决什么问题,以及它和 C 系指针最关键的区别。
很多人第一次学 Go 指针时,会带着两种相反的情绪:
- 学过 C 的人会觉得它“太温和了”
- 没学过底层语言的人会觉得它“已经够危险了”
这两种感觉都可以理解。Go 里的指针既保留了“通过地址访问数据”的能力,又刻意拿掉了很多容易把程序写崩的部分。
背景
指针本质上不是“高级语法”,而是一个很直接的概念:
某个值在内存中的位置。
Go 里引入指针,主要是为了解决两个问题:
- 避免大对象频繁复制
- 允许你修改原对象,而不是只改副本
也就是说,指针不是为了“炫技”,而是为了明确表达共享和修改。
核心概念
&:取地址
如果你想拿到一个变量的地址,用 &:
1x := 10
2p := &x
这里:
x是一个intp是一个*int
也就是“指向 int 的指针”。
*:解引用
如果你想通过指针拿到它指向的值,就要解引用:
1fmt.Println(*p) // 10
如果你给解引用结果赋值,改的就是原对象:
1*p = 20
2fmt.Println(x) // 20
这就是指针最核心的能力:
不是复制一份数据出来改,而是直接改原来的那份数据。
一个最小示例
1package main
2
3import "fmt"
4
5func main() {
6 x := 10
7 p := &x
8
9 fmt.Println("x =", x)
10 fmt.Println("*p =", *p)
11
12 *p = 42
13 fmt.Println("x =", x)
14}
运行逻辑很清楚:
- 先拿到
x的地址 - 再通过指针读取
x - 最后通过指针修改
x
指针和参数传递
这部分是很多人最容易混的地方。
Go 始终是值传递。
即使你传的是指针,本质上也还是“把这个指针值复制一份传进去”。
但因为这个值本身指向原对象,所以函数里可以通过它修改原对象。
例如:
1package main
2
3import "fmt"
4
5func inc(n *int) {
6 *n++
7}
8
9func main() {
10 x := 1
11 inc(&x)
12 fmt.Println(x) // 2
13}
这里不是“Go 变成了引用传递”,而是“复制了一份指针值进去,这份指针仍然指向原来的 x”。
指针最常见的使用场景
1. 修改结构体内容
1type User struct {
2 Name string
3}
4
5func rename(u *User, name string) {
6 u.Name = name
7}
如果这里不用指针,函数里改到的就是结构体副本。
2. 避免大对象复制
当结构体比较大时,用指针传递可以减少复制开销。
但这不是绝对规则,只有在语义和性能都合适时才值得这么做。
3. 表达“可空”状态
例如一个字段如果需要区分:
- 没有值
- 值就是 0
这时常会用指针表达可选值。
Go 指针和 C 指针最关键的区别
Go 刻意砍掉了几样危险能力:
- 不支持指针算术
- 不能像 C 那样随意偏移地址
- 不能任意把指针当整数乱算
这让 Go 指针的使用边界更小,也更可控。
所以 Go 指针更像“安全一点的对象访问入口”,而不是“随便操作内存的工具”。
常见误区
误区一:以为有指针就一定更快
不一定。
指针能减少复制,但也会引入:
- 更多共享状态
- 更复杂的生命周期
- 可能的逃逸和额外 GC 压力
所以不能机械地认为“全都改成指针就会更高效”。
误区二:忘记 nil 指针
指针类型的零值是 nil。
如果你直接解引用一个 nil 指针,程序会 panic:
1var p *int
2fmt.Println(*p) // panic
误区三:把“传指针”理解成“引用传递”
Go 不是引用传递。
只是因为你传进去的是一个能找到原对象的地址,所以看起来像“直接改到了外面”。
指针和方法接收者
Go 里还有一个非常常见的指针场景,就是方法接收者:
1type Counter struct {
2 N int
3}
4
5func (c *Counter) Inc() {
6 c.N++
7}
这里用指针接收者,通常是因为:
- 方法需要修改接收者
- 或者希望避免复制整个结构体
如果方法不修改状态,是否必须用指针接收者,就要结合类型大小和语义一起看。
我的总结
- Go 指针的本质是“通过地址访问原对象”
&取地址,*解引用- Go 依然是值传递,传指针也不例外
- 指针最常用于修改对象、避免大对象复制、表达可空值
- Go 指针比 C 更克制,没有指针算术
- 真正用好指针,关键不在语法,而在于清楚什么时候应该共享、什么时候应该复制