




传指针更省内存,因只拷贝8字节指针而非整个结构体;可修改原数据、明确函数副作用,但需防nil解引用和逃逸导致堆分配。
Go 中函数参数是值传递,哪怕传的是结构体,也会完整拷贝一份。如果结构体很大(比如含切片、map 或大量字段),拷贝开销明显。传 *T 只拷贝一个指针(通常 8 字节),不管 T 多大,成本恒定。
常见错误现象:func processUser(u User) { u.Name = "modified" } 修改无效——因为改的是副本;而 func processUser(u *User) { u.Name = "modified" } 能生效,且无额外内存分配。
[]byte、map[string]int、sync.Mutex 等不可复制字段的类型,必须用指针(否则编译报错 cannot use ... as type ... in argument to ... (invalid memory address or nil pointer dereference))int)传值反而更快——避免解引用和缓存未命中,别盲目全用指针函数签名中出现 *T 是一种明确的契约:该函数可能修改调用方的数据。这比靠文档或命名(如 UpdateUser)更可靠,也方便静态分析工具识别副作用。
使用场景:
立即学习“go语言免费学习笔记(深入)”;
json.Unmarshal 必须传 *T)func (u *User) SetID(id int) { u.ID = id })T 或 interface{},而非 *T——否则调用方无法从签名判断是否安全传 *T 带来便利,但也引入 nil 风险。一旦函数内直接解引用未判空的指针,运行时报错 panic: runtime error: invalid memory address or nil pointer dereference。
实操建议:
if u == nil { return errors.New("user cannot be nil") } 或直接 
panic(仅限内部工具函数)func DoThing(opt *Options) { if opt == nil { opt = &Options{Timeout: 30} } }
传指针不等于变量一定分配在堆上,但会显著提高逃逸概率。Go 编译器通过逃逸分析决定变量位置:如果函数返回了指向参数的指针,或将其存入全局变量、channel、map,该变量就会逃逸到堆。
性能提示:
go build -gcflags="-m" main.go 查看逃逸情况,关注类似 ... escapes to heap 的输出[]int 已经是“轻量级指针语义”,通常无需再传 *[]int
真正要小心的不是“用不用指针”,而是“这个指针背后的数据生命周期是否可控”。很多性能问题源于没看清数据到底落在哪,而不是指针本身。