首页 / Go 语言入门教程 / 指针基础

Go 语言入门教程

指针基础

本教程共 80 篇 · 第 36 篇 · 更新于 2026-07-27 · 约 7 分钟阅读

GoGo 入门教程指针Pointer取址解引用new

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 里组织数据的核心方式。