结构体指针是刚性需求而非可选优化:必须用指针的三大场景是修改原字段、避免大结构体复制开销、匹配指针接收者方法;new(T)与&T{}均返回*T但初始化语义不同;接口断言和嵌套指针字段需严格类型匹配,否则panic。

结构体指针不是“可选优化”,而是 Go 中修改原值、避免拷贝、匹配方法接收者类型的刚性需求。用错类型(比如该传 *Person 却传 Person)会导致方法调用静默失效或 panic。
什么时候必须用结构体指针
三个典型场景:需要在函数内修改结构体字段、结构体较大(避免复制开销)、方法接收者是指针类型。
- 如果结构体有 10 个字段,传值会复制全部字段;传
*Person只传一个地址(通常 8 字节) - 定义了
func (p *Person) SetName(name string),那只有*Person能调用它;Person值类型调用会编译失败 - 像
net.Conn、sql.DB这类资源型结构体,内部字段多含指针或 sync.Mutex,必须用指针操作,否则字段为零值
结构体指针的初始化方式差异
new(T) 和 &T{} 都返回 *T,但语义和内存分配位置不同,影响 nil 判断和字段初始化逻辑。
-
peo := new(Person):在堆上分配零值内存,peo永远不为nil,所有字段为零值(peo.Name == "",peo.Age == 0) -
peo := &Person{Name: "Alice"}:显式初始化部分字段,未写的字段仍为零值;若写成&Person{},效果等同new(Person) -
var peo *Person:声明但未初始化,peo是nil,直接解引用(如peo.Name)会 panic
接口断言时结构体指针的类型匹配
接口变量里存的是 *Dog,断言就必须用 dog, ok := animal.(*Dog);写成 dog, ok := animal.(Dog) 永远失败,ok 为 false。
立即学习“go语言免费学习笔记(深入)”;
- 原因:接口底层存储的是动态类型 + 值。存指针时,类型信息就是
*Dog,不是Dog - 常见错误:遍历
[]interface{}时对每个元素做.(*MyStruct)断言,却忘了检查ok,导致 panic - 安全写法:
if dog, ok := item.(*Dog); ok { dog.Bark() } else if cat, ok := item.(*Cat); ok { cat.Meow() }
嵌套结构体中指针字段的初始化陷阱
结构体字段本身是指针类型(如 name *string),不显式分配内存就解引用,运行时直接崩溃。
- 错误写法:
type Student struct { Name *string } s := Student{} *s.Name = "Alice" // panic: nil pointer dereference - 正确做法之一(栈上):
name := "Alice" s := Student{&name} - 正确做法之二(堆上):
s := Student{} s.Name = new(string) *s.Name = "Alice" - 关键点:字段指针和结构体指针是两回事——前者是字段类型,后者是结构体实例的地址
最易被忽略的是:结构体字面量(如 Person{Name:"A"})不可寻址,不能对其取地址;而 &Person{Name:"A"} 返回的指针是可寻址且有效的。很多 runtime panic 其实源于把临时字面量当成了可修改对象。


















