必须用...展开切片,否则编译失败;不赋值接收返回值,追加就等于没做。因为append只接受类型T的变参,不接受[]T;错误写法s=append(s,otherSlice)缺...,正确为s=append(s,otherSlice...);nil切片解包安全;append返回新header,原变量不变,须显式赋值。

必须用 ... 展开源切片,否则编译失败;不赋值接收返回值,追加就等于没做。
为什么 append(dst, src) 会编译报错
因为 append 是变参函数,签名是 func append(slice []T, elems ...T) []T,它只接受零个或多个类型为 T 的值,不接受 []T 类型的切片本身。
常见错误现象:cannot use src (type []int) as type int in append
- 错误写法:
s = append(s, otherSlice)—— 缺少...,编译器把整个切片当做一个int值传入 - 正确写法:
s = append(s, otherSlice...)——...将otherSlice解包成elem1, elem2, elem3等独立参数 -
...只能用于切片,不能用于数组(除非先转成切片:arr[:]...) - 若
otherSlice是nil,append(s, otherSlice...)安全且等价于s(Go 运行时已优化)
append 不赋值就等于没追加
切片是 header 值类型(含指针、len、cap),append 返回的是新 header,原变量不会被修改。
立即学习“go语言免费学习笔记(深入)”;
常见错误现象:写了 append(s, 1) 但没赋值,fmt.Println(len(s)) 还是旧长度;编译器会警告 result of append not used。
- 必须显式赋值:
s = append(s, 1)或s = append(s, 1, 2, 3) - 函数内修改需返回:
func add(s []int, x int) []int { return append(s, x) },调用方必须接住返回值 - 传参也是 header 副本:函数里
append(s, x)不影响调用方的s,但改s[i] = y会影响(因指针未变)
底层数组复用带来的隐式共享
只要容量足够(len(s) ),<code>append 会复用原底层数组——性能好,但容易引发意外修改。
典型症状:两个切片 a 和 b := a[1:3],对 a 执行 a = append(a, x) 后,b 的内容可能跟着变。
- 判断是否扩容:比较
cap是否变化,len(s) == cap(s)时下一次append必定扩容 - 调试技巧:
fmt.Printf("%p", &s[0])打印首元素地址,两次调用间变了说明底层数组已迁移 - 需要隔离时,别用
s[:0](仍复用底层数组),改用newS := append([]int(nil), s...) - 存进
map后再append更危险:map value 是 header 副本,但若未扩容,仍指向原数组
预估容量可减少扩容次数
频繁扩容会触发多次内存分配与拷贝,尤其在循环中追加大量数据时。
常见错误做法:每次循环都 s = append(s, x),导致 cap 持续翻倍或按 1.25 倍增长(≥1024 后),但实际只需刚好够用的空间。
- 推荐写法:
s = make([]int, 0, estimatedTotal)预分配,再循环append - 若无法预估,可用
s = append(s[:0], s...)清空并保留底层数组(注意:这不改变cap) - 对
[]byte追加多行时,scanner.Bytes()已含换行符,直接content = append(content, scanner.Bytes()...)即可
最易被忽略的是:扩容策略虽有规律,但 Go 不承诺精确容量值;你唯一能依赖的,只有 len 和 cap 的关系、append 返回值必须显式接收、以及 ... 不可省略这三个事实。


















