一、 为什么指针总让人“血压飙升”?
很多新手听到“指针”二字就头大,仿佛回到被C++指针支配的校园时代。但Go的指针其实是个“温和版刺客”——能直接操作内存,却不会让你随便搞崩系统。
想象你搬家时有两种选择:
- 传值:把家具全部复制一份给朋友(费时费内存)
- 传指针:直接把新家地址发给朋友(高效省资源)
Go指针就是那个“地址纸条”,让你用最小代价操控数据老巢。接下来咱们用三分钟,把这张纸条的玩法摸透!
二、 指针基础:从「宇宙符号」*和&说起
1. 两个核心操作符
&(取址符):好比查户口,获取变量在内存的住址*(解引用符):好比用钥匙开门,直接访问地址里的值
2. 声明指针的三种姿势
姿势一:标准宣言
var ptr *int // 声明一个指向int类型的指针
这时的ptr是nil,就像一张没写地址的空纸条。
姿势二:就地取材
age := 25
ptr := &age // 把age的地址赋给ptr
此时ptr指向age的家,*ptr能掏出值25。
姿势三:new()函数造房
ptr := new(int) // 直接分配一块int内存,返回指针
*ptr = 30 // 给这个地址赋值
new()相当于地产开发商,直接给你个毛坯房地址。
三、 完整示例:指针实战情景剧
下面我们演一出《指针变形记》,注意看代码注释:
package main
import "fmt"
func main() {
// 场景1:基本操作 - 跟踪变量豪宅
house := "别墅"
var address *string = &house // 获取豪宅地址
fmt.Printf("房子类型:%T,值:%s\n", house, house) // 输出:房子类型:string,值:别墅
fmt.Printf("地址类型:%T,地址值:%p\n", address, address) // 输出地址(十六进制)
fmt.Printf("通过地址看房子:%s\n", *address) // 输出:别墅
// 场景2:修改门牌 - 指针改变原值
*address = "海景大平层"
fmt.Println("翻修后的房子:", house) // 输出:海景大平层
// 场景3:空指针警戒区 - 别敲NULL的门!
var emptyPtr *string
if emptyPtr == nil {
fmt.Println("空指针警告!别直接解引用")
// *emptyPtr = "炸弹" // 放开这行会panic!
}
// 场景4:函数传参性价比之战
price := 100
doubleFail(price)
fmt.Println("传值操作后:", price) // 还是100,原值未变
doubleReal(&price)
fmt.Println("传指针操作后:", price) // 变成200,直捣老巢
}
// 传值:收到的是副本
func doubleFail(money int) {
money *= 2
}
// 传指针:拿到保险箱钥匙
func doubleReal(money *int) {
*money *= 2 // 修改原值
}
运行结果:
房子类型:string,值:别墅
地址类型:*string,地址值:0xc000010050
通过地址看房子:别墅
翻修后的房子: 海景大平层
空指针警告!别直接解引用
传值操作后: 100
传指针操作后: 200
四、 避坑指南:指针玩家的自我修养
坑1:空指针解引用(经典闪退)
var p *int
fmt.Println(*p) // panic: runtime error!
防御策略:
if p != nil {
fmt.Println(*p) // 安全操作
}
坑2:指针越界(小心隔壁老王)
Go的内存安全机制会阻止越界访问,但别故意试探编译器底线。
坑3:函数内返回局部变量指针
func danger() *int {
num := 10
return &num // 编译器其实会逃逸分析,但别依赖!
}
虽然Go有逃逸分析保底,但逻辑上这仍是危险操作。
五、 进阶技巧:当指针遇上结构体和切片
1. 结构体指针:直击对象软肋
type Player struct {
Name string
Level int
}
p := &Player{"菜鸟", 1}
p.Level = 99 // 等价于 (*p).Level,Go自动解引用甜点
fmt.Println(p.Name, "秒变满级大佬")
2. 切片的小秘密
切片本身是“胖指针”,包含数组指针+长度+容量:
data := []int{1,2,3}
slicePtr := &data[0] // 获取底层数组首地址
但直接操作切片指针较少见,因为切片已自带引用语义。
六、 总结:指针使用心法
- 声明三式:
var p *T、p := &v、p := new(T) - 核心口诀:
&取址,*取值,nil指针别解引用 - 使用场景:
-
- 大结构体传参避免复制
- 需要修改原值
- 实现链表/树等数据结构
- Go特色:无指针运算,安全系数++
最后送个彩蛋:尝试用指针实现一个「变量交换器」,彻底告别临时变量!指针不是洪水猛兽,而是你代码库里的空间魔术师。多写多练,让内存操作成为你的超能力!
附录:思考题
- 如何用指针实现一个精简版链表?
- 函数返回指针时,编译器背后做了哪些优化?

被折叠的 条评论
为什么被折叠?



