指针
约 972 字大约 3 分钟
2026-03-25
指针保存的是变量在内存中的地址。通过指针可以间接读取或修改这个地址上的值,因此它常用于共享数据、避免大对象拷贝,以及在函数中修改原变量
Go 中日常使用的指针通常就是 *T 这种类型指针。切片虽然内部持有对底层数组的引用,但切片本身不是普通意义上的指针类型
指针的声明与基本使用
声明一个指针变量时,需要写明它指向的数据类型。例如 *int 表示 "指向 int 的指针",*string 表示 "指向 string 的指针"
package main
import "fmt"
func main() {
num := 10
var p *int
p = &num
fmt.Println("num:", num)
fmt.Println("p:", p)
fmt.Println("*p:", *p)
*p = 20
fmt.Println("num after change:", num)
}提示
指针变量本身也有零值。未初始化的指针默认值为 nil,表示当前没有指向任何地址
& 与 *
在指针相关的语法中,& 和 * 最常见:
&变量:获取变量的地址*指针:获取指针指向地址上的值,也叫“解引用”var p *int:声明一个“指向int的指针变量”
取地址与解引用
package main
import "fmt"
func main() {
name := "gopher"
p := &name
fmt.Println("address:", p)
fmt.Println("value:", *p)
*p = "Go"
fmt.Println("name:", name)
}重要
* 在不同位置含义不同:写在类型前表示“指针类型”,写在变量前表示“取出该指针指向的值”。
nil 指针
指针在没有指向任何变量时,其值为 nil。nil 指针可以比较,但不能解引用,否则会触发运行时错误。
判断 nil 指针
package main
import "fmt"
func main() {
var p *int
fmt.Println(p == nil)
if p != nil {
fmt.Println(*p)
}
}提示
在解引用前,如果指针可能为空,应该先判断是否为 nil。
指针作为函数参数
Go 的函数参数默认是值传递,也就是说函数拿到的是实参的一份拷贝。如果希望在函数内部直接修改原变量,就可以传入指针。
通过指针修改函数外部变量
package main
import "fmt"
func addOne(n *int) {
(*n)++
}
func main() {
count := 10
addOne(&count)
fmt.Println(count)
}如果函数只需要读取数据而不修改原值,就不一定非要使用指针。是否使用指针,主要取决于是否需要修改原数据,以及是否希望减少值拷贝成本。
new 关键字
new(T) 会分配一个类型为 T 的零值变量,并返回它的地址,返回值类型是 *T。它适合需要一个“已分配但还是零值”的对象场景。
new 的基本使用
package main
import "fmt"
func main() {
p := new(int)
fmt.Println(*p)
*p = 42
fmt.Println(*p)
}对于结构体,更常见的写法通常是 &User{},因为它既能得到指针,又能直接完成字段初始化。
指针与结构体
结构体和指针经常一起使用。拿到结构体指针后,可以直接通过 . 访问字段,Go 会自动帮你完成解引用,因此 u.Name 等价于 (*u).Name。
结构体指针访问字段
package main
import "fmt"
type User struct {
Name string
Age int
}
func main() {
u := &User{
Name: "Alice",
Age: 18,
}
u.Age++
fmt.Printf("%+v\n", u)
}提示
方法接收器如果是指针类型,那么通常意味着这个方法会修改原结构体,或者希望避免拷贝整个结构体。
指针的限制
Go 保留了指针的核心能力,但刻意削弱了它的“危险操作”。最典型的一点是:Go 不支持像 C 语言那样直接进行指针运算。
// 不支持这样的写法
// p++
// p = p + 1这样设计的目的,是降低内存操作出错的概率,让代码更安全、可维护。
