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

124 lines
3.4 KiB
Markdown
Raw Blame History

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