Go切片变量仅持24字节header(指针、len、cap),不持有数据,赋值开销恒定;数组变量直接持有连续数据块,长度是类型一部分,传参时全量拷贝。

数组变量直接持有数据,切片变量只持有一个 header 结构
Go 中 [5]int 类型的变量本身就是一个连续的 5 个 int 占用的内存块,变量值就等于那块数据。而 []int 类型的变量实际只存三个字段:Data(指针)、Len、Cap,它不持有数据,只是描述「哪段底层数组、取多长」。
这意味着:
-
var a [1000]int在栈上分配 8000 字节(假设 int64),传参时整个拷贝 -
var s []int = make([]int, 1000)变量本身只占 24 字节(ptr+len+cap),传参只复制这 24 字节 - 多个切片可能共享同一底层数组,改一个会影响另一个——因为
Data指向的是同一地址
切片扩容时底层数组可能迁移,导致指针失效
当你对切片反复 append,超过当前 Cap 时,Go 运行时会分配一块新内存(通常是原容量的 2 倍或 1.25 倍),把旧数据 memcpy 过去,再更新切片的 Data 和 Cap。此时原底层数组未被引用后会被 GC 回收。
常见踩坑点:
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 从某个数组创建切片后,又对该切片大量
append→ 底层数组可能已换,原数组不再受影响 - 把切片传给函数,在函数内
append后没返回新切片 → 调用方看到的仍是旧切片(Len没变,Data可能已变但没同步) - 用
unsafe.Slice或反射绕过类型系统操作切片时,若忽略Cap边界,容易读到脏内存或触发 panic
数组长度是类型的一部分,切片不是
[3]int 和 [4]int 是完全不同的类型,不能赋值、不能传给同一个参数(哪怕都叫「int 数组」)。而所有 []int 都是同一类型,不管长度或容量多少。
这个差异直接影响:
- 函数签名:你无法写一个接受「任意长度数组」的通用函数,但可以写
func f(s []int) - 接口实现:切片能轻松满足
io.Writer等接口;数组不行,除非包装成切片 - map key:数组可作为 map key(如
map[[3]int]string),切片不行(无定义相等性)
小数组在栈上初始化,大数组可能触发静态区拷贝
编译器对数组初始化做了优化:元素 ≤4 个的小数组直接在栈上构造;≥5 个则先在静态存储区初始化一份模板,再 runtime.copy 到栈上。这对性能影响不大,但调试时你会看到栈帧里多了一次 memcpy。
切片不受此限制——make([]int, n) 总是在堆上分配底层数组(除非逃逸分析判定可栈分配),而切片 header 本身总在栈上(或寄存器中)。
真正要注意的是:
- 用
var a [1024]byte做 buffer 时,函数栈帧瞬间膨胀 1KB,可能触发栈扩容甚至栈溢出 - 用
make([]byte, 1024)更安全,header 小,底层数组由 runtime 统一管理 - 如果确定生命周期短且 size 固定,
[N]byte配合copy操作反而比切片少一次指针解引用
Data,那段内存就可被清理。

















