3.4 KiB
3.4 KiB
tags, create time
| tags | create time | ||||
|---|---|---|---|---|---|
|
2026-06-07 15:00 |
Go 语言指针
概述
指针是存放内存地址的变量。Go 保留了这一底层能力,但做了简化——去掉了指针运算,只保留 &(取地址)和 *(解引用)。本文解释指针的工作原理、new() vs make(),以及何时该用指针。
正文
指针到底是什么?
简单说:指针 = 内存地址。它本身不存储数据,而是指向存储数据的位置。
b := 42 // b 在内存某处存了值 42
ptr := &b // ptr 存的是 b 的地址(如 0x14000018000)
fmt.Println(*ptr) // 通过地址读取 → 42
*ptr = 100 // 通过地址修改 → b 变成了 100
| 操作符 | 含义 | 示例 |
|---|---|---|
&T |
取变量的地址 | &b → *int |
*T |
解引用指针 | *ptr → 读取/写入指向的值 |
[!question] ❓ 思考 如果
a := &b,那么*a = 100会影响b吗?为什么?
创建指针的两种方式
方式一:& 取地址
var ptr *int
b := 3
ptr = &b // ptr 指向 b 的地址
fmt.Println(*ptr) // 3
未初始化的指针值为 nil:
var p *int
fmt.Println(p == nil) // true
方式二:new() 分配零值内存
p := new(int) // 分配 int 大小的内存,归零,返回 *int
*p = 42
fmt.Println(*p) // 42
[!note] 📝 new(T) 做了什么?
- 分配一块大小为
sizeof(T)的内存- 将内存清零(零值)
- 返回
*T指向这块内存等价于
&T{},但后者更常用且可内联初始化。
new() vs make():最常见的混淆
这是 Go 面试必问的问题:
new(T) |
make(T, ...) |
|
|---|---|---|
| 适用类型 | 所有类型 | 仅限 slice、map、chan |
| 返回值 | *T(指针) |
T(值本身) |
| 用途 | 分配零值内存 | 分配+初始化 |
// new:只分配,不归零以外的初始化
p := new(int) // *int,指向 0
s := new([]int) // *[]int,指向 nil slice(不能直接用!)
// make:分配 + 初始化
sl := make([]int, 3, 5) // len=3, cap=5 的切片
mp := make(map[string]int) // 可用的空 map
ch := make(chan int, 10) // 缓冲为 10 的 channel
[!warning] ⚠️ new([]int) 是陷阱
new([]int)返回的是*[]int,里面的 slice 头是 nil。对它做append会 panic。应该用make。
结构体与指针
type Point struct {
X, Y int
}
p := Point{X: 1, Y: 2}
ptr := &p
ptr.X = 10 // Go 自动解引用,等价于 (*ptr).X = 10
fmt.Println(p.X) // 10 — 原值被修改了
[!tip] 💡 什么时候传指针?
- 需要修改原值:函数参数传
*T- 结构体较大:避免拷贝开销(通常 > 64 字节就值得考虑)
- slice/map/chan:它们本身就是引用语义,不需要额外加指针
常见误区
[!warning] ⚠️ 不要对 map 元素取地址
m := map[string]int{"a": 1} p := &m["a"] // ❌ 编译错误:cannot take address of map elementmap 的元素不是稳定的内存位置——扩容时整个数据结构会迁移。
[!info] ℹ️ Go 没有指针运算 Go 去掉了 C 语言的
ptr++、ptr + n等指针算术。你只能:
- 取地址
&v- 解引用
*p- 比较
p == nil这消除了缓冲区溢出等安全漏洞。