go 的 slice 是三字段结构体,s[i:j] 仅计算新地址、len 和 cap 而不分配内存;三索引切片 s[a:b:c] 的 c 是底层数组绝对结束索引,cap = c - a,影响 append 复用但不影响访问。

Go 的 slice 变量本身就是一个三字段值,不是指针、不隐式解引用、不自动分配内存——它就是 uintptr + int + int 三个字宽的结构体,直接在栈上操作。
为什么 s[i:j] 不分配内存,也不影响底层数组?
因为这个操作只做三件事:算新起始地址、设新 len、算新 cap。整个过程不 new、不 malloc、不碰 GC 堆。
-
s[i:j]的新Data地址 = 原Data+i * unsafe.Sizeof(T) - 新
len=j - i - 新
cap= 原cap-i(注意:不是j - i,也不是j) - 如果原 slice 来自
make([]int, 5, 10),那么s[2:4]的cap是10 - 2 = 8,不是 10 或 4
make([]T, len, cap) 中 cap 控制的是“能往哪写”,不是“能读到哪”
cap 决定的是 append 能否复用底层数组,和索引访问完全无关。越界读写仍由 len 管。
-
make([]byte, 3, 10):你可以append7 次不扩容;但s[5]会 panic,因为len == 3 -
cap小于len是非法的,编译器直接报错:cap is smaller than len - 省略
cap时(如make([]int, 5)),cap == len,后续第一次append就触发扩容
三索引切片 s[a:b:c] 的 c 是绝对偏移,不是容量值
s[a:b:c] 的第三个参数 c 是底层数组的**绝对结束索引**(即 s[c] 不可访问),不是你想要的容量数字。
-
s := []int{0,1,2,3,4,5}(len=6, cap=6) -
s[1:3:5]→ 新len = 2,新cap = 5 - 1 = 4(从索引 1 开始算,到索引 5 结束,共 4 个可用位置) - 误写成
s[1:3:4]会导致cap = 3,比预期少一个位置,append更早扩容 - 这个语法唯一用途是人为压低
cap,防止意外append越界污染相邻数据(比如传给不信任的函数)
真正容易被忽略的点在于:cap 是相对起始偏移计算的,不是配置项也不是魔法数字;每次切片都会重算,且只影响 append 行为,对 [] 访问、len、遍历全无影响。写错 cap 不会立即报错,但会在某次 append 后悄悄扩容,破坏性能预期。











