Go指针是安全可控的内存引用,声明为T,需避免nil解引用;传指针可修改原变量,slice/map/channel本身是引用类型,无需额外加。

Go 语言中的指针不是“C 风格的危险工具”,而是明确、可控的内存地址引用——只要不越界解引用或传 nil,它就是安全的。
如何声明和初始化一个指针变量
Go 的指针类型语法是 *T,表示“指向类型 T 的值的地址”。声明时必须指定所指类型,不能像 C 那样用 void* 混用。
常见错误:直接对未初始化的指针解引用(panic: runtime error: invalid memory address)。
-
var p *int声明了一个 int 类型指针,但此时p == nil,不能直接写*p = 10 - 正确初始化方式之一:
num := 42; p := &num,&取地址,p现在指向num的内存位置 - 也可用 new():
p := new(int),等价于var v int; p := &v,返回已分配零值的地址
函数参数传指针 vs 传值的区别
Go 默认按值传递——函数内修改形参不会影响实参。想让函数修改原始变量,必须传指针。
立即学习“go语言免费学习笔记(深入)”;
典型场景:修改结构体字段、避免大对象拷贝、实现“输出参数”语义。
- 传值:
func modify(x int) { x = 100 }→ 调用后原变量不变 - 传指针:
func modify(x *int) { *x = 100 }→ 必须用*x解引用赋值 - 结构体传指针更常见:
func (u *User) setName(n string) { u.name = n },否则方法无法修改接收者字段
nil 指针检查与常见 panic 场景
Go 不做空指针自动防护,*p 在 p == nil 时立即 panic。这要求你主动判断,而不是依赖运行时兜底。
容易踩坑的地方:函数返回指针但没检查是否为 nil;接口变量底层是 nil 指针却直接调用方法。
- 安全解引用前务必检查:
if p != nil { val := *p } - 从 map、channel、slice 中取值再取地址?注意它们可能返回零值,
&m["key"]在 key 不存在时会取到&zeroValue,但该地址有效;而m["key"]本身是零值,取地址没问题,但若原 map 值是 struct 指针,要小心嵌套 nil - 方法接收者为指针时,nil 接收者调用方法不会 panic —— 除非方法内部访问了
*receiver字段(比如receiver.field)
切片、map、channel 本身已经是引用类型,不需要额外加 *
新手常误以为“要修改 slice 就得传 *[]T”,其实完全没必要。slice 本身包含指向底层数组的指针、长度和容量,传 slice 就是传这三个字段的副本,修改元素或用 append 改变底层数组内容都生效;但若想改变 slice 头部(如让它指向新数组),才需要传 *[]T。
-
func update(s []int) { s[0] = 999 }→ 原 slice 第一个元素被改 -
func reassign(s *[]int) { *s = []int{1,2,3} }→ 原 slice 变成新切片 - map 和 channel 同理:传
map[string]int即可增删改查;只有想让函数替换整个 map 变量(比如清空并重置为新 map)才需*map
package main
import "fmt"
type User struct {
Name string
}
func main() {
age := 25
p := &age
fmt.Println(*p) // 25
// 修改原变量
*p = 26
fmt.Println(age) // 26
u := User{Name: "Alice"}
pu := &u
pu.Name = "Bob" // 等价于 (*pu).Name = "Bob"
fmt.Println(u.Name) // Bob
}
指针真正的复杂点不在语法,而在你是否清楚某个值的生命周期是否覆盖指针的使用范围——比如返回局部变量地址(Go 编译器会自动逃逸分析并堆分配,通常安全),但手动管理 C 内存或 CGO 场景下,这点就极易出错。










