
go 中切片是值传递,函数内无法直接修改调用方原始切片的长度(len)和容量(cap);必须传入切片指针(*[]t)才能实现原地收缩并同步更新 len/cap。
go 中切片是值传递,函数内无法直接修改调用方原始切片的长度(len)和容量(cap);必须传入切片指针(*[]t)才能实现原地收缩并同步更新 len/cap。
在 Go 语言中,切片(slice)虽包含指向底层数组的指针、长度和容量三个字段,但作为参数传递时仍是值传递——即函数接收的是该切片结构体的一份副本。因此,即使你在函数内部对切片执行 s = s[:n] 这类操作,仅会修改副本的 len 字段,不会影响调用方原始变量的 len 或 cap,更不会改变其底层数组引用关系。
如示例所示,原始 rmDup 函数返回新切片,但 main 中的 str 变量仍保持原长度(7)和容量(7),输出中 len(str) 仍为 7,证明其未被真正“缩容”。
✅ 正确做法:*传递切片指针 `[]string** 通过解引用str获取原始切片值,操作后将结果重新赋值给str`,即可同步更新调用方变量的长度与容量:
func rmDup(str *[]string) []string {
s := *str // 解引用:获取当前切片值
for i := 1; i < len(s); i++ {
if s[i] == s[i-1] {
copy(s[i:], s[i+1:]) // 向前覆盖重复元素
s[len(s)-1] = "" // 清零末尾残留(可选,提升内存安全性)
s = s[:len(s)-1] // 缩短长度
}
}
*str = s // 关键:将修改后的切片写回原地址
return s
}
func main() {
str := []string{"dog", "cat", "cat", "mouse", "mouse", "zebra", "zebra"}
fmt.Println("Before:", str) // [dog cat cat mouse mouse zebra zebra]
rmDup(&str) // 注意取地址 &
fmt.Println("After: ", str) // [dog cat mouse zebra]
fmt.Printf("len=%d, cap=%d\n", len(str), cap(str)) // len=4, cap=7
}⚠️ 注意事项:
-
cap(str)不会因s = s[:n]自动缩减——容量始终由底层数组决定,除非重新切分到更小底层数组或使用make分配新切片; - 若需彻底释放内存(如避免大底层数组被小切片长期持有),应显式创建新切片:
newSlice := append([]string(nil), s...); - 多次删除时注意索引越界风险:上述相邻去重逻辑在删除后未调整
i,可能导致跳过下一个元素;生产环境建议改用双指针或反向遍历。
总结:Go 中“修改外部切片尺寸”本质是修改其结构体副本中的 len 字段,唯有通过指针才能让这一修改反映到原始变量上。理解切片的三元结构(ptr, len, cap)及其值传递语义,是写出安全、高效切片操作代码的关键。

















