copy函数只按切片视图忠实地复制元素,不判断业务逻辑合理性:重叠时安全覆盖,长度不匹配时默默截断;要求dst已分配空间,否则返回0;支持重叠内存的自动安全方向处理;仅浅拷贝,含引用字段需手动深拷贝;不保证底层数组隔离,需显式分配新数组。

copy 函数不会帮你判断业务逻辑是否合理,它只忠实地按你给的切片视图复制元素——传进去重叠区间,它就安全地覆盖;传进去长度不匹配,它就默默截断。别指望它“聪明”,但可以完全信任它不崩、不越界、不乱序。
copy(dst, src) 要求 dst 必须已分配空间
常见错误是声明一个 nil 切片然后直接 copy:
var dst []int
src := []int{1, 2, 3}
n := copy(dst, src) // n == 0,dst 仍是 nil
这是因为 copy 只复制 min(len(dst), len(src)) 个元素,而 len(dst) 是 0。
- 正确做法:用
make([]T, len(src))预分配目标切片 - 如果 dst 容量大于 src,多出的空间保持原值(不会被清零)
- 如果 dst 长度小于 src,只复制前
len(dst)个,不 panic
重叠内存时 copy 自动选安全方向,但结果取决于你的切片表达式
比如实现左移一位:copy(s, s[1:]),或右移填充:copy(s[1:], s),都能安全执行——底层类似 memmove,不是 memcpy。
- 不需要手动判断从前往后还是从后往前遍历
- 但你要清楚自己写的
s[i:j]和s[k:l]在底层数组上是否真重叠、重叠多少 - 例如
a := []int{1,2,3,4,5}; copy(a[2:], a[1:4])后,a变成[1 2 2 3 4],这不是 bug,是你明确让索引 2 开始写、索引 1 开始读、共 3 个元素 - 若
dst == src(自拷贝),copy直接返回len(src),不执行任何内存操作
copy 只做浅拷贝,对指针/结构体字段不做递归处理
当你复制 []*int 或 []struct{ Data []byte } 时,copy 只复制指针值或 []byte 头(即 array、len、cap 三元组),不复制它们指向的内容。
立即学习“go语言免费学习笔记(深入)”;
- 修改
dst[i].Data[0]会同时改src[i].Data[0],因为两者Data字段仍指向同一底层数组 - 对纯值类型(如
[]int、[]string)没问题;含引用字段的结构体必须手动深拷贝每个字段 - 用
json.Marshal + json.Unmarshal模拟深拷贝不可靠:丢失未导出字段、func、channel、unsafe.Pointer,且性能差
如何真正隔离底层数组?别只靠 copy
copy(dst, src) 本身不保证目标切片的底层数组是全新的——如果 dst 是从 src 截取来的(如 dst := src[2:4]),那它们仍共享底层数组。
- 安全做法是两步:先
make([]T, len(src))分配新底层数组,再copy - 或者一步到位:
dst := append([]T(nil), src...),利用append对 nil 切片的特殊行为触发新分配 - 验证是否真正隔离:改
dst后用reflect.DeepEqual(src, original)确认原数据没变,再改original,确认dst不联动
copy 的安全性是 runtime 层面的,但“是否该重叠”“是否要深拷贝”“副本后续会不会被 append 扩容污染”,这些都得靠你代码里显式表达,它不会替你做业务决策。


















