在 Go语言切片拷贝 的学习过程中,很多初学者会混淆“浅拷贝”和“深拷贝”的概念。本文将从基础讲起,详细解释 Go 语言中切片的 copy 函数如何工作,并进一步介绍如何实现真正的 深拷贝,帮助你彻底掌握 Go slice copy 的核心技巧。
在 Go 语言中,切片(slice)是对底层数组的一个动态视图。它包含三个部分:指向底层数组的指针、长度(len)和容量(cap)。正因为切片内部包含指针,所以在赋值或传递时,多个切片可能共享同一块底层数组。
copy 函数进行浅拷贝Go 提供了内置函数 copy(dest, src []T),用于将一个切片的内容复制到另一个切片中。但要注意:这只是一个浅拷贝(shallow copy),即只复制元素的值,不复制其内部引用类型(如指针、结构体中的指针字段等)。
package mainimport "fmt"func main() { src := []int{1, 2, 3, 4, 5} dest := make([]int, len(src)) n := copy(dest, src) fmt.Printf("复制了 %d 个元素\n", n) fmt.Println("源切片:", src) fmt.Println("目标切片:", dest)} 输出:
复制了 5 个元素源切片: [1 2 3 4 5]目标切片: [1 2 3 4 5]
此时,dest 和 src 是两个独立的切片,修改其中一个不会影响另一个——因为它们是基本类型(int)的切片。但如果切片元素是引用类型(比如指针或结构体),情况就不同了。
type Person struct { Name string Age int}func main() { src := []*Person{ {"Alice", 30}, {"Bob", 25}, } dest := make([]*Person, len(src)) copy(dest, src) // 修改 dest 中第一个元素的 Name dest[0].Name = "Charlie" fmt.Println("src[0].Name:", src[0].Name) // 输出: Charlie fmt.Println("dest[0].Name:", dest[0].Name) // 输出: Charlie} 可以看到,虽然我们使用了 copy,但由于切片存储的是指针,src 和 dest 共享同一个 Person 对象。这就是浅拷贝的局限性。
要实现 Go深拷贝,我们需要手动遍历切片,并为每个元素创建新的副本。对于结构体,通常需要逐字段复制。
func deepCopyPersons(src []*Person) []*Person { dest := make([]*Person, len(src)) for i, p := range src { // 为每个 Person 创建新实例 dest[i] = &Person{ Name: p.Name, Age: p.Age, } } return dest}func main() { src := []*Person{ {"Alice", 30}, {"Bob", 25}, } dest := deepCopyPersons(src) dest[0].Name = "Charlie" fmt.Println("src[0].Name:", src[0].Name) // 输出: Alice fmt.Println("dest[0].Name:", dest[0].Name) // 输出: Charlie} 现在,修改 dest 不会影响 src,因为我们真正复制了数据本身,而不仅仅是指针。
对于复杂结构,可以借助序列化(如 JSON)来实现通用深拷贝(注意:仅适用于可导出字段且支持 JSON 序列化的类型):
import ( "encoding/json")func DeepCopy(src interface{}) interface{} { b, _ := json.Marshal(src) var dst interface{} json.Unmarshal(b, &dst) return dst} 不过这种方法性能较低,且有类型限制,建议仅在简单场景使用。生产环境中更推荐手动编写深拷贝逻辑或使用第三方库(如 github.com/jinzhu/copier)。
copy 函数执行的是浅拷贝,适用于基本类型切片。希望这篇 Go语言教程 能帮你彻底理解切片拷贝机制!
本文由主机测评网于2025-12-12发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/2025126582.html