应仅在需表达“未设置”或“函数内修改原值”时用*int;否则因拷贝成本低、指针增开销与panic风险,优先使用int。
对 Go 中的 int、bool、float64、string 等基本类型,**绝大多数场景下使用指针没有意义,反而增加风险和开销**。只有极少数明确需要“可空性”或“函数内修改原值”的场景才值得考虑。
*int 而不是 int?核心判断依据就两个:是否需要表达“未设置”,或是否必须在函数内修改调用方的原始变量。
Age *int 可以区分 {"age":0}(显式设为 0)和 {}(字段缺失),而 Age int 两者都解出 0
func parseTimeout(s string, out *time.Duration) error,让调用方传入一个地址,解析成功后直接写入——这比返回 time.Duration 再赋值更符合某些 API 设计习惯(但非常规)type Config struct { MaxRetries *int },允许调用方不设置该字段,后续逻辑通过 if c.MaxRetries != nil 判断是否启用重试*int?因为 Go 的基本类型本
身很小(int 通常是 8 字节),拷贝成本几乎为零;而指针引入了额外间接层、nil 检查负担、GC 压力,还容易引发 panic。
int 是栈上复制 8 字节;传 *int 是复制 8 字节地址 + 额外一次内存寻址 + 潜在的堆分配(如果指针指向的是 new 出来的)nil,运行时直接 panic —— var p *int fmt.Println(*p) // panic: runtime error: invalid memory address or nil pointer dereference
string 本身是只读的 header 结构(含指针),传 *string 并不能让你“修改原字符串内容”,只能替换整个字符串值,且不如直接返回新 string 清晰开发者常因“想模仿其他语言的引用传递”或“看到别人用了就跟着用”,导致写出脆弱代码。
立即学习“go语言免费学习笔记(深入)”;
*string 只能改它指向哪个字符串,无法像 C 那样改底层字节数组var user struct{ Name *string }
fmt.Println(*user.Name) // panic*int:每个 new(int) 都是一次堆分配,小对象堆化会显著抬高 GC 压力*int 参数,另一个却用 int,调用方容易传错,且无法通过编译器强制统一真正需要指针语义时,优先考虑是否能用值类型 + 明确返回值替代;只有当“空/非空”语义不可省略(如数据库 NULL 映射、API 可选字段),才引入基本类型的指针——而且务必在文档和构造逻辑中明确其生命周期与 nil 含义。