append 返回新切片,原变量不变;因切片值传递,内部结构体被拷贝;必须显式赋值接收返回值,否则修改无效;容量足够时复用底层数组,不足时分配新数组。

Go 语言中 append 不是“往切片末尾加元素”这么简单——它可能复用底层数组,也可能分配新数组;原切片变量不更新,必须显式赋值;追加后原变量可能失效。这是绝大多数初学者踩坑的根源。
为什么 append 后原切片没变?
append 返回的是一个新切片(包含可能更新的 ptr、len、cap),它不会修改传入的切片变量本身。Go 的切片是值传递,内部结构体(指针+长度+容量)被拷贝了。
常见错误写法:
data := []int{1, 2}
append(data, 3) // ❌ 什么也没发生,data 还是 [1 2]
fmt.Println(data) // 输出 [1 2]
正确做法:
- 必须接收返回值:
data = append(data, 3) - 如果在函数内操作,需返回新切片或用指针传参(但通常不推荐指针传切片)
- 多处追加时,每次都要赋值,不能省略
append 会修改原底层数组吗?
取决于容量是否足够。若 len(s) ,<code>append 复用底层数组;否则分配新数组,并将原数据复制过去。
这意味着:
- 两个共享底层数组的切片,其中一个
append后若未扩容,可能意外影响另一个(尤其当它们重叠且未满容时) - 用
make([]int, 0, 10)预分配容量,可减少扩容次数,也更易预测行为 - 调试时可用
fmt.Printf("%p", &s[0])看底层数组首地址是否变化
追加多个元素或另一个切片怎么写?
支持单个元素、多个字面量、或展开另一个切片(用 ...)。
合法写法:
s := []int{1}
s = append(s, 2) // 单个
s = append(s, 3, 4, 5) // 多个字面量
t := []int{6, 7}
s = append(s, t...) // 展开切片(注意 ... 不是省略号,是语法)
容易错的地方:
-
append(s, t)会把整个切片t当作一个元素追加(类型不匹配报错) -
t...中的...必须紧贴变量名,不能有空格:t ...是错的 - 追加空切片
append(s, []int{}...)安全,等价于不追加
性能和边界情况要注意什么?
append 的扩容策略是:容量翻倍(小容量)或增长 25%(大容量),具体由运行时决定。但你不该依赖这个规律。
关键点:
- 频繁小量
append(如循环中每次加 1 个)会导致多次内存分配,建议预估长度并用make初始化 -
append(nil, x)是合法的,结果为[]T{x},常用于初始化 - 对
nil切片追加不会 panic,但对其取s[0]会 panic - 追加后检查
len和cap是否符合预期,尤其在内存敏感场景下
最常被忽略的一点:你写的 append 语句,只要没把返回值赋给某个变量,就等于没执行。这不是语法错误,是逻辑废操作——编译器甚至不会警告。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











