go语言学习笔记 - 数组与切片
数组(Array)是定长的值类型。是一段连续的内容
切片(Slice)是动态的引用类型。是一个结构体
数组(Array)
var arr [3]int = [3]int{1, 2, 3}
特点:
- 固定长度:
[3]int
是一个长度为 3 的数组类型。 - 值类型:传参/赋值时会 拷贝整个数组(不共用内存)。
- 类型包含长度:
[3]int
和[4]int
是完全不同的类型。
切片(Slice)
var s []int = []int{1, 2, 3}
特点:
- 动态长度:可以增删扩容。
- 引用类型:底层指向数组,传参时只是复制了引用结构(共用内存)。
- 包含结构:切片底层是一个结构体:
扩容机制
Go 切片扩容规则(简化理解):
- 小于 1024:通常 每次扩容容量 ×2
- 大于 1024:每次大约按 1.25 倍增长
- 扩容时会 新建更大的底层数组并拷贝内容(旧数组可能变为垃圾,等待 GC)
s := make([]int, 0)
for i := 0; i < 10; i++ {
s = append(s, i)
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
}
底层结构
type slice struct {
ptr unsafe.Pointer // 指向底层数组的指针
len int // 当前切片长度
cap int // 容量(cap 可增长)
}
常见坑点
- 切片截取仍引用原数组,容易出现数据共享问题:
b := a[:3]
b[0] = 999
fmt.Println(a) // [999 2 3 4 5]
- append 后不一定是同一个数组(因为可能触发了扩容)
小技巧:避免在共享切片上直接 append;并发读写时加锁
a := []int{1, 2, 3}
b := append(a, 4)
b[0] = 100
fmt.Println(a) // [1 2 3],a 没变,因为 append 触发了扩容