在Go语言的实际开发中,当我们需要批量处理多个自定义结构体实例时,使用结构体指针切片是比普通结构体切片更高效的选择,它可以减少值拷贝带来的内存开销,尤其适合处理大结构体或者需要频繁修改结构体内容的场景。

自定义结构体指针切片的基础创建
首先我们需要先定义一个自定义结构体,再声明对应的指针切片。和普通切片声明类似,只需要在类型前加上*标识即可。
// 定义自定义结构体
type User struct {
ID int
Name string
Age int
}
func main() {
// 声明结构体指针切片
var userList []*User
// 初始化空切片,也可以通过字面量初始化
userList = make([]*User, 0, 10)
// 字面量初始化方式
userList2 := []*User{
&User{ID: 1, Name: "张三", Age: 20},
&User{ID: 2, Name: "李四", Age: 22},
}
}
向指针切片中添加元素
向结构体指针切片中添加元素时,需要先创建结构体实例的指针,再通过append函数追加到切片中。
func main() {
userList := make([]*User, 0, 10)
// 创建结构体指针
user1 := &User{ID: 3, Name: "王五", Age: 25}
// 追加到切片
userList = append(userList, user1)
// 也可以直接追加匿名结构体指针
userList = append(userList, &User{ID: 4, Name: "赵六", Age: 28})
}
遍历和访问指针切片元素
遍历结构体指针切片时,可以通过索引访问或者for range遍历,访问元素属性时直接用->的简写形式,也就是用点号访问指针指向的结构体字段。
func main() {
userList := []*User{
&User{ID: 1, Name: "张三", Age: 20},
&User{ID: 2, Name: "李四", Age: 22},
}
// 索引访问
firstUser := userList[0]
println(firstUser.Name) // 输出 张三
// for range遍历
for index, user := range userList {
println(index, user.ID, user.Name, user.Age)
}
}
修改指针切片中的元素
因为切片中存储的是结构体指针,所以修改元素属性时,会直接修改原结构体实例的内容,不需要重新赋值给切片。
func main() {
userList := []*User{
&User{ID: 1, Name: "张三", Age: 20},
}
// 修改第一个元素的年龄
userList[0].Age = 21
println(userList[0].Age) // 输出 21
// 也可以通过指针变量修改
targetUser := userList[0]
targetUser.Name = "张三三"
println(userList[0].Name) // 输出 张三三
}
使用注意事项
- 避免空指针访问:切片中的元素如果是未初始化的指针,直接访问字段会导致程序 panic,访问前可以先判断指针是否为空。
- 切片扩容问题:
append操作可能触发切片扩容,扩容后底层数组地址会变化,如果之前有保存切片元素的指针,需要注意指针是否还指向有效内容。 - 结构体指针的生命周期:只要指针切片还引用着结构体指针,对应的结构体实例就不会被垃圾回收,不需要额外担心实例被提前释放的问题。
func main() {
userList := make([]*User, 0)
// 错误示例:追加空指针
// userList = append(userList, nil)
// println(userList[0].Name) // 会触发panic
// 正确做法:先判断指针是否为空
if userList[0] != nil {
println(userList[0].Name)
}
}