Golang中的结构体是组合多个不同类型字段的复合数据类型,当结构体体积较大时,直接传递结构体实例会产生复制开销,使用指针操作结构体字段和引用结构体地址能显著提升程序性能。理解指针与结构体的结合用法是Golang开发的基础技能之一。

Golang指针与结构体基础概念
指针是存储变量内存地址的变量,在Golang中通过*声明指针类型,通过&获取变量的内存地址。结构体指针就是指向结构体实例内存地址的指针,通过结构体指针可以直接访问和修改结构体的字段。
首先定义一个简单的结构体作为后续示例的基础:
package main
import "fmt"
// 定义用户信息结构体
type User struct {
Name string
Age int
Score float64
}
通过指针操作结构体字段
获取结构体实例的指针
可以通过&操作符直接获取结构体实例的内存地址,得到结构体指针:
func main() {
// 创建结构体实例
user := User{
Name: "张三",
Age: 20,
Score: 88.5,
}
// 获取结构体实例的指针
userPtr := &user
fmt.Printf("user实例地址: %pn", userPtr)
fmt.Printf("userPtr类型: %Tn", userPtr) // 输出 *main.User
}
通过指针访问和修改结构体字段
Golang提供了自动解引用的特性,通过结构体指针访问字段时不需要手动加*,直接通过指针变量.字段名即可操作:
func main() {
user := User{
Name: "张三",
Age: 20,
Score: 88.5,
}
userPtr := &user
// 通过指针修改字段值
userPtr.Name = "李四"
userPtr.Age = 22
userPtr.Score = 92.0
// 通过指针访问字段值
fmt.Println("修改后姓名:", userPtr.Name)
fmt.Println("修改后年龄:", userPtr.Age)
fmt.Println("修改后分数:", userPtr.Score)
// 验证原实例也被修改
fmt.Println("原实例姓名:", user.Name)
}
如果需要手动解引用,也可以通过(*指针变量).字段名的方式操作,效果和自动解引用一致:
func main() {
user := User{Name: "张三", Age: 20, Score: 88.5}
userPtr := &user
// 手动解引用修改字段
(*userPtr).Name = "王五"
fmt.Println("手动解引用修改后姓名:", user.Name)
}
Golang结构体地址引用方法
方法接收者使用结构体指针
定义结构体方法时,如果接收者使用结构体指针类型,那么方法内部可以修改结构体的字段,并且调用方法时不需要复制整个结构体:
// 指针接收者方法,修改用户年龄
func (u *User) UpdateAge(newAge int) {
u.Age = newAge
}
// 值接收者方法,仅返回年龄加1的结果,不修改原结构体
func (u User) GetNextAge() int {
return u.Age + 1
}
func main() {
user := User{Name: "张三", Age: 20, Score: 88.5}
fmt.Println("初始年龄:", user.Age) // 输出20
// 调用指针接收者方法,修改原结构体字段
user.UpdateAge(25)
fmt.Println("修改后年龄:", user.Age) // 输出25
// 调用值接收者方法,不影响原结构体
nextAge := user.GetNextAge()
fmt.Println("下一年年龄:", nextAge) // 输出26
fmt.Println("调用后原年龄:", user.Age) // 输出25
}
函数参数传递结构体指针
当函数需要操作结构体字段时,传递结构体指针可以避免大结构体的复制开销,同时可以在函数内部修改结构体的内容:
// 函数接收结构体指针参数,修改分数
func UpdateScore(u *User, newScore float64) {
u.Score = newScore
}
func main() {
user := User{Name: "张三", Age: 20, Score: 88.5}
fmt.Println("初始分数:", user.Score) // 输出88.5
UpdateScore(&user, 95.0)
fmt.Println("修改后分数:", user.Score) // 输出95
}
使用new函数创建结构体指针
Golang内置的new函数可以分配内存并返回对应类型的指针,对于结构体类型,new(结构体类型)会返回该结构体的指针,字段会被初始化为零值:
func main() {
// 使用new创建User结构体指针
userPtr := new(User)
fmt.Printf("userPtr类型: %Tn", userPtr) // 输出 *main.User
// 零值初始化,Name为空字符串,Age为0,Score为0
fmt.Println("初始Name:", userPtr.Name)
fmt.Println("初始Age:", userPtr.Age)
// 直接操作指针字段
userPtr.Name = "赵六"
userPtr.Age = 30
userPtr.Score = 89.0
fmt.Println("赋值后信息:", userPtr)
}
指针操作结构体的注意事项
- 结构体指针为
nil时,访问字段会触发运行时 panic,操作前需要判断指针是否为空:
func main() {
var userPtr *User
if userPtr != nil {
fmt.Println(userPtr.Name)
} else {
fmt.Println("指针为空,无法访问字段")
}
}
- 小结构体如果不需要修改字段,使用值传递即可,指针传递的额外开销可能比复制小结构体更高。
- 结构体指针也支持比较操作,两个指针指向同一个实例时相等,指向不同实例时不等,nil指针之间也相等。
常见使用场景总结
| 场景 | 推荐操作方式 |
|---|---|
| 需要修改结构体字段内容 | 使用结构体指针操作,或定义指针接收者方法 |
| 传递大体积结构体作为参数 | 传递结构体指针,避免复制开销 |
| 仅读取结构体字段,不需要修改 | 小结构体用值传递,大结构体用指针传递但避免修改 |
| 需要返回结构体实例且后续可能修改 | 返回结构体指针,方便后续操作 |