Files
cs-note/hzh/GolangStar/Go语言基础/Go语言指针.md
T

124 lines
3.4 KiB
Markdown
Raw Normal View History

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