golang slice
slice 是 golang 的切片,动态数组
底层结构
// runtime/slice.go
type slice struct {
array unsafe.Pointer // 底层数组
len int
cap int
}
golang 的 slice 底层是一个数组,也就是可以通过 &s[0]
来获取底层数组的地址,len 记录的当前,cap 记录的是底层数组的大小也就是当前 slice 的容量
初始化
var num1 []int // num1此时为 nil
num2 := make([]int,4) //len = 4 cap = 4
num3 := make([]int,3,7) // len = 3 cap = 7
num4 := []int{1,2,3} // len = 3 cap = 3
或从另一个slice新建
s1 := []int{1,2,3,4,5} // s1 len = 5 cap = 5
s2 := s1[:] // s2 len = 5 cap = 5
s3 := s1[:3] // s3 len = 3 cap = 5
s4 := s1[:2:4] // s4 len = 2 cap = 4
以上的 s2,s3,s4 底层数组指向的都是 s1 的底层数组起始位置
函数传参
slice 作为函数传参时候传递的是 slice 的 struct 的拷贝,但是底层数组是作为struct 内部的一个变量是不变的,所以在函数间传递时候更改 slice 的某些变量会影响原 slice
扩容
在 slice 调用 append 方法时,如果添加后的容量大于 cap,那么就会发生扩容
扩容规则
在1.18版本之前
当原 cap < 1024 时候,每次扩容会扩容成两倍;当 cap > 1024 时候,会扩容成原容量的 1.25 倍
在1.18 版本之后
当原 cap < 256 时候,每次扩容会扩容成原容量的两倍;当 cap > 256,会扩容成原容量的 newcap = oldcap+(oldcap+3*256)/4 倍,所以当原容量是 256 时,也会扩容成 512;但是在实际扩容过程中不会精确地这个值,通常会进行一次内存对齐,所以会有出入;
扩容过程
先申请 newcap 大小的数组,然后把原数组全量拷贝过去,然后再进行其他的操作
难点
slice 的难点主要是还是传参,和传参后的 append 触发扩容
基本原则就一个,没发生扩容的情况下,会改变原数组;如果发生扩容,则不会改变原数组
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)