在Go语言中,结构体是常用的数据聚合类型。如果不使用指针,函数接收到的是结构体的副本,对字段的修改不会影响原变量。通过指针可以直接操作原内存地址,从而实现真正的数据修改。
为什么需要结构体指针
Go的函数参数传递都是值传递。当把结构体变量传给函数时,系统会复制一份。修改副本不会影响调用方的原数据。使用指针可以把结构体的内存地址传进去,函数内通过地址找到原数据并修改。
值传递的问题示例
package main
import "fmt"
type User struct {
Name string
Age int
}
// 值接收者,修改的是副本
func growUp(u User) {
u.Age = u.Age + 1
}
func main() {
u := User{Name: "Tom", Age: 20}
growUp(u)
fmt.Println(u.Age) // 输出 20,原数据未变
}
使用指针修改结构体数据
把函数参数改为结构体指针,或在方法中使用指针接收者,就能修改原数据。声明指针用 *User,取地址用 & 符号,访问字段可用 -> 或点号。
指针作为参数修改
package main
import "fmt"
type User struct {
Name string
Age int
}
// 指针接收者,修改原数据
func growUp(u *User) {
u.Age = u.Age + 1
}
func main() {
u := User{Name: "Tom", Age: 20}
growUp(&u)
fmt.Println(u.Age) // 输出 21,原数据已改
}
指针接收者方法
为结构体定义方法时,若想修改字段,接收者应为指针类型。
package main
import "fmt"
type User struct {
Name string
Age int
}
// 指针接收者方法
func (u *User) growUp() {
u.Age = u.Age + 1
}
func main() {
u := User{Name: "Tom", Age: 20}
u.growUp()
fmt.Println(u.Age) // 输出 21
}
指针与值的使用对比
| 场景 | 值类型 | 指针类型 |
|---|---|---|
| 传参开销 | 复制整个结构体 | 只复制地址 |
| 修改原数据 | 不能 | 能 |
| 适用情况 | 小型只读结构 | 需修改或大结构 |
注意事项
- 声明指针后使用前必须初始化,避免空指针异常。
- 结构体指针零值为
nil,直接访问字段会报错。 - Go会自动处理
(*p).Field和p.Field的等价写法,推荐用点号更简洁。
合理使用结构体指针,既能节省内存拷贝,又能安全地修改业务数据。