在Golang的开发过程中,slice是常用的数据结构,而结构体指针的使用也能带来不少便利,将两者结合使用是很多场景下的常见需求,比如需要批量管理多个结构体实例并频繁修改其属性时,在slice中存放结构体指针会比存放结构体值更高效。
slice存放结构体指针的基础实现
首先我们来看最基础的存放方式,先定义结构体,再创建结构体指针,最后将指针放入slice中。下面是具体的代码示例:
package main
import "fmt"
// 定义用户结构体
type User struct {
ID int
Name string
Age int
}
func main() {
// 创建存放User结构体指针的slice
var userPtrSlice []*User
// 创建第一个用户指针并加入slice
user1 := &User{ID: 1, Name: "张三", Age: 20}
userPtrSlice = append(userPtrSlice, user1)
// 创建第二个用户指针并加入slice
user2 := &User{ID: 2, Name: "李四", Age: 22}
userPtrSlice = append(userPtrSlice, user2)
// 遍历slice打印内容
for _, u := range userPtrSlice {
fmt.Printf("用户ID:%d,姓名:%s,年龄:%dn", u.ID, u.Name, u.Age)
}
}
两种存放方式的对比
在slice中存放元素时,我们可以选择存放结构体值,也可以选择存放结构体指针,两者的差异主要体现在以下几个方面:
| 对比维度 | 存放结构体值 | 存放结构体指针 |
|---|---|---|
| 内存开销 | 每个元素存储完整的结构体内容,拷贝时需要复制全部字段 | 每个元素仅存储指针地址,拷贝时仅复制地址,开销更小 |
| 修改影响 | 修改slice中的元素不会影响原始结构体实例 | 修改slice中的指针指向的结构体内容,会影响所有指向该实例的指针 |
| 适用场景 | 结构体体积较小,不需要修改元素内容,或者需要元素相互独立的场景 | 结构体体积较大,需要频繁修改元素内容,或者需要多个地方共享同一结构体实例的场景 |
常见错误与规避方法
在使用slice存放结构体指针时,有一个非常常见的错误,就是在循环中创建临时变量并取地址放入slice,导致所有指针都指向同一个地址,最终slice中的所有元素值都相同。下面是错误示例:
package main
import "fmt"
type Item struct {
Val int
}
func main() {
var itemPtrSlice []*Item
// 错误示例:循环中取临时变量的地址
for i := 0; i < 3; i++ {
tmp := Item{Val: i}
itemPtrSlice = append(itemPtrSlice, &tmp)
}
// 打印结果会发现所有元素的Val都是2
for _, item := range itemPtrSlice {
fmt.Println(item.Val)
}
}
上述错误的原因是循环中的tmp变量是复用的,每次迭代都会更新同一个变量的内容,取地址后得到的是同一个地址,所以所有指针都指向同一个tmp变量,最终值都是最后一次迭代的结果。
规避这个错误的方法有两种,一种是在循环内部创建新的结构体实例,不要复用临时变量:
package main
import "fmt"
type Item struct {
Val int
}
func main() {
var itemPtrSlice []*Item
// 正确方式1:每次循环创建新的实例
for i := 0; i < 3; i++ {
// 每次迭代都创建新的Item实例,取地址后放入slice
newItem := Item{Val: i}
itemPtrSlice = append(itemPtrSlice, &newItem)
}
for _, item := range itemPtrSlice {
fmt.Println(item.Val)
}
}
另一种方式是直接创建指针类型的实例,避免地址复用的问题:
package main
import "fmt"
type Item struct {
Val int
}
func main() {
var itemPtrSlice []*Item
// 正确方式2:直接创建指针实例
for i := 0; i < 3; i++ {
itemPtrSlice = append(itemPtrSlice, &Item{Val: i})
}
for _, item := range itemPtrSlice {
fmt.Println(item.Val)
}
}
实际使用策略建议
在实际开发中,选择是否在slice中存放结构体指针可以参考以下策略:
- 如果结构体的体积很小,比如只有几个基础类型的字段,且不需要修改slice中的元素内容,优先选择存放结构体值,代码更简洁,也不容易出现指针相关的错误。
- 如果结构体体积较大,或者需要频繁修改slice中元素的内容,优先选择存放结构体指针,减少内存拷贝的开销。
- 如果需要在多个地方共享同一个结构体实例,比如slice中的元素和其他模块的变量指向同一个实例,那么必须使用结构体指针,否则修改其中一个不会影响另一个。
- 在循环中向slice添加结构体指针时,一定要注意避免临时变量地址复用的问题,优先使用直接创建指针实例的方式。
总结
在Golang的slice中存放结构体指针并不复杂,核心是理解结构体值和结构体指针的差异,以及循环中变量地址复用的陷阱。合理选择存放方式,既能提升代码的性能,也能避免不必要的错误。在实际开发中,可以根据结构体的大小、是否需要修改、是否需要共享实例等维度来判断使用哪种方式,让代码更高效、更健壮。
Golangslicestruct_pointerslice使用策略修改时间:2026-07-21 00:18:34