指针基础
本教程共 80 篇 · 第 36 篇 · 更新于 2026-07-27 · 约 7 分钟阅读
36. 指针基础
本节目标:搞懂指针是什么,学会用 & 取地址、用 * 取值,了解 new 函数和 nil 指针,知道 Go 指针为什么不能做运算。
指针是什么
变量存在内存里,每个变量都有一个内存地址。指针就是存这个地址的变量。
打个比方:变量是一间房子,里面放着数据。指针是房子的门牌号,你拿着门牌号就能找到这间房子,进而拿到里面的东西。
package main
import "fmt"
func main() {
x := 42
p := &x // p 是指向 x 的指针,& 是取地址
fmt.Println(x) // 42
fmt.Println(p) // 0xc000012028 这是 x 的内存地址
fmt.Println(*p) // 42 * 是解引用,通过指针取值
}
&x:取变量 x 的地址,得到一个指针*p:通过指针 p 取出它指向的值
& 取地址,* 取值
这两个操作符是配套的,互相反操作:
x := 42
p := &x // & 取地址:从值得到指针
v := *p // * 取值:从指针得到值
fmt.Println(v) // 42
通过指针还能修改原变量的值:
package main
import "fmt"
func main() {
x := 42
p := &x
*p = 100 // 通过指针修改 x 的值
fmt.Println(x) // 100 x 变了!
}
Note
*出现在类型前面时表示「指针类型」,出现在变量前面时表示「解引用」。比如*int是「指向 int 的指针类型」,*p是「取出 p 指向的值」。看上下文区分。
指针的零值:nil
指针的零值是 nil,表示「不指向任何地址」:
var p *int
fmt.Println(p) // <nil>
fmt.Println(p == nil) // true
对 nil 指针解引用会 panic:
var p *int
fmt.Println(*p) // panic: runtime error: invalid memory address or nil pointer dereference
Warning这是 Go 里最常见的 panic 之一。用指针之前一定要确保它不是 nil。
new 函数
new(T) 分配一个 T 类型的零值内存,返回指向它的指针:
package main
import "fmt"
func main() {
p := new(int) // 分配一个int的零值,返回*int
fmt.Println(*p) // 0 int的零值
*p = 42
fmt.Println(*p) // 42
}
new(int) 等价于:
var x int
p := &x
实际开发中 new 用得不多,因为 &Type{} 这种写法更常用。
Go 1.26 的 new(expr)
Go 1.26 给 new 加了个新能力:可以直接传入一个表达式作为初始值,省去先声明变量再取地址的麻烦:
// Go 1.26 之前
age := 30
p := &age
// Go 1.26 新写法
p := new(30) // 直接返回指向 30 的 *int
这在处理 JSON 可选字段等需要指针字面量的场景特别方便,§14 有更详细的例子。
Tip
new(expr)是 Go 1.26 的新特性。老版本需要先声明变量再取地址,Go 1.26 让这个过程更简洁了。
Go 指针不能做运算
这是 Go 和 C 语言指针最大的区别。在 C 里你可以 p++ 让指针移动到下一个内存位置,Go 不行:
x := [3]int{10, 20, 30}
p := &x[0]
p++ // 编译错误!Go 不允许指针运算
p = &x[1] // 只能重新取地址
Go 这样设计是有意的。指针运算容易导致内存越界和安全问题,去掉它让 Go 更安全,也让垃圾回收更好实现。
指针的常见用途
函数传参
Go 函数参数默认是值传递(拷贝一份)。如果你想让函数修改外部变量,传指针:
package main
import "fmt"
func addOne(n int) {
n++ // 改的是副本,不影响外部
}
func addOnePtr(n *int) {
*n++ // 通过指针修改外部变量
}
func main() {
x := 10
addOne(x)
fmt.Println(x) // 10 没变
addOnePtr(&x)
fmt.Println(x) // 11 变了
}
避免大结构体拷贝
如果结构体很大,传值会拷贝整份数据,影响性能。传指针只拷贝一个地址:
type BigStruct struct {
Data [1000]int
}
// 传值:每次拷贝1000个int
func process(b BigStruct) { ... }
// 传指针:只拷贝一个地址
func processPtr(b *BigStruct) { ... }
小结
指针是 Go 里绕不开的概念,记住这几点:
&取地址,*取值,两者互为逆操作- 指针零值是 nil,解引用 nil 会 panic
new(T)返回指向零值的指针- Go 没有指针运算,更安全
- 函数传指针可以修改外部变量,也能避免大对象拷贝
下一章讲结构体,Go 里组织数据的核心方式。