学习目标
从根本上理解 指针解决的是什么问题
明确 Go 中“值传递”与“共享修改”的边界
掌握 `&`、`*`、`new`、`nil` 的真实含义
知道在工程中 什么时候必须用指针,什么时候不该用
一、为什么 Go 需要指针
在 Day 2 我们已经确认一个事实:
Go 中 赋值、传参、返回,默认都是值拷贝
那么问题来了:
如果我就是想修改外部变量,怎么办?
答案只有一个: 指针(pointer)
二、什么是指针(最准确的定义)
指针是一个变量,保存的是“另一个变量的内存地址”
a := 10
p := &a此时:
- a:值是 10
- &a:a 在内存中的地址
- p:类型是 *int,值是 a 的地址
- *p:通过地址访问 a 本身
三、指针的两种基本操作
1. 取地址(&)
a := 10
p := &a含义:
把变量 a 的内存地址取出来
2. 解引用(*)
fmt.Println(*p) // 10
*p = 20
fmt.Println(a) // 20过程说明:
- p 保存的是地址
- *p 表示“访问该地址指向的值”
- 修改 *p,就是修改原变量
结论:
指针 = 间接访问 + 共享修改
四、指针与函数参数(最核心用法)
1. 不使用指针(无法修改)
func change(x int) {
x = 100
}
func main() {
a := 10
change(a)
fmt.Println(a) // 10
}原因:
- x 是 a 的拷贝
2. 使用指针(可以修改)
func change(x *int) {
*x = 100
}
func main() {
a := 10
change(&a)
fmt.Println(a) // 100
}过程说明:
- &a 把 a 的地址传入
- x 保存地址
- *x = 100 修改原值
结论:
Go 没有“引用传参”,只有“传地址的值”
五、指针与 struct(工程中最常见)
1. struct 作为参数(值拷贝)
type User struct {
Name string
}
func change(u User) {
u.Name = "Jack"
}
func main() {
u := User{Name: "Tom"}
change(u)
fmt.Println(u.Name) // Tom
}2. struct 指针作为参数(推荐)
func change(u *User) {
u.Name = "Jack"
}
func main() {
u := User{Name: "Tom"}
change(&u)
fmt.Println(u.Name) // Jack
}结论(非常重要):
- struct 参与修改 → 必须用指针
- struct 较大 → 必须用指针
- 方法接收者 → 90% 用指针
六、方法接收者:值 vs 指针(必会)
type User struct {
Name string
}
func (u User) SetName1(name string) {
u.Name = name
}
func (u *User) SetName2(name string) {
u.Name = name
}func main() {
u := User{Name: "Tom"}
u.SetName1("Jack")
fmt.Println(u.Name) // Tom
u.SetName2("Jack")
fmt.Println(u.Name) // Jack
}结论:
- 要修改对象 → 指针接收者
- Go 会自动帮你解引用(语法糖)
七、new 与 & 的区别
1. 使用 new
p := new(int)
*p = 10含义:
- 分配一块内存
- 初始化为零值
- 返回指针
2. 使用 &(更常见)
x := 10
p := &x对比总结
| 写法 | 是否分配内存 | 是否初始化 | 常用场景 |
|---|---|---|---|
| new(T) | 是 | 是(零值) | 很少直接用 |
| &x | 已存在 | 已有值 | 最常用 |
八、nil 指针(高频 panic 来源)
1. 错误示例
var p *int
*p = 10 // panic原因:
- p 没有指向任何内存
2. 正确写法
p := new(int)
*p = 10或:
x := 10
p := &x结论:
指针必须“指向有效内存”才能解引用
九、Go 的指针是“安全的”
Go 刻意限制了指针能力:
- ❌ 没有指针运算(p++ 不存在)
- ❌ 不能随意偏移内存
- ❌ 不能手动释放内存
- ✅ 有 GC(垃圾回收)
结论:
Go 的指针是“工程安全指针”,不是 C 指针
十、什么时候该用指针?(工程判断标准)
必须用指针的场景
- 需要修改变量
- struct 较大(避免拷贝)
- 方法接收者
- 表示“可选值”(nil / 非 nil)
不该用指针的场景
- int / bool / time.Duration
- map / slice / chan(本身是引用语义)
- 只读小对象
十一、自检问题(进入 Day 4 前必须能回答)
- Go 为什么坚持值传递?
- 指针到底解决了什么问题?
- new 和 & 的区别是什么?
- map / slice 为什么通常不用指针?
- 方法接收者如何选择?
预告:Day 4
Day 4|struct 设计、组合与嵌入
你将学会:
- 如何用 struct 设计业务模型
- 嵌入(embedding)的真正用途
- 如何用组合替代继承
- 工程中 struct 的正确“边界感”