结构体变量直接持有全部字段数据,拷贝时深复制;结构体指针仅存8字节地址,拷贝开销极小;值接收者方法两者皆可调,指针接收者方法只能由指针调用。

结构体变量和结构体指针在内存中的本质区别
结构体变量直接持有所有字段数据,分配在栈(或逃逸到堆)上,值拷贝时会复制全部字段;结构体指针只保存一个地址,拷贝时仅复制 8 字节(64 位系统)的指针值。这意味着:func f(s MyStruct) 传值会深拷贝整个结构体,而 func f(s *MyStruct) 传指针几乎无开销。
方法集差异导致调用失败的常见错误
Go 的方法集规则决定了哪些接收者能调用哪些方法:以值为接收者的方法,MyStruct 和 *MyStruct 都能调用;但以指针为接收者的方法(如 func (s *MyStruct) Update()),只有 *MyStruct 能直接调用,MyStruct 变量调用会报 cannot call pointer method on s 错误。
- 如果结构体含 slice/map/channel/func/interface 或需修改原值,必须用指针接收者
- 小结构体(如两个 int)用值接收者更高效;大结构体(含 []byte、嵌套结构等)务必用指针接收者
-
var s MyStruct; s.Update()会编译失败,需写成(&s).Update()或声明为s := &MyStruct{}
字段访问语法看似一致,底层行为完全不同
无论 s := MyStruct{X: 1} 还是 s := &MyStruct{X: 1},写 s.X 都能通过编译——Go 编译器自动解引用指针。但这只是语法糖,背后逻辑不同:
-
s.X对值变量:直接读取字段偏移地址 -
s.X对指针变量:先加载指针值,再按偏移读取字段(一次内存寻址变两次) - 若指针为
nil,s.X会 panic:invalid memory address or nil pointer dereference - 想明确区分,可用
(*s).X强制解引用,但极少需要
初始化和赋值时的隐式转换陷阱
Go 允许在多数上下文里混用结构体变量和指针,但几个边界场景极易出错:
立即学习“go语言免费学习笔记(深入)”;
- 数组/切片元素类型是
MyStruct,却试图存入&MyStruct{}→ 类型不匹配,编译失败 - map 的 value 类型是
*MyStruct,却用m["k"] = MyStruct{}→ 编译报错,必须显式取地址:m["k"] = &MyStruct{} - 使用
new(MyStruct)得到的是*MyStruct,且字段全零值;&MyStruct{}效果相同,但可带字面量初始化 - JSON 解码时,
json.Unmarshal(b, &s)必须传指针,否则字段不会被修改(因为传的是副本)
type Point struct{ X, Y int }。别被自动解引用迷惑,时刻清楚你手里的到底是个“东西”还是个“东西的地址”。









