Slice
basic
type SliceHeader struct {
Data uintptr //切片数据域指针,声明了该切片在内存中起始地址
Len int
Cap int
}
- 切片内存:在 64 位机器下,最小分配内存单元是 8 字节(内存分配必须是 2 的指数倍),实际切片分配的内存与数据域类型直接相关
- 切片分配的内存由 3 部分共同组成,data 域大小,len,cap,且每一部分内存宽度必须是 2 的倍数(尽管只使用 1 个字节也会按这样规则分配,为了保证内存对齐)
- 切片扩容:切片扩容内存会重新分配,扩容后切片元素个数增加,容量改变
- 扩容容量(元素个数)计算方式:
- 扩容后容量 newCap 大于 2 倍起始容量(2oldCap),则容量为 newCap
- 元素个数小于 1024 时,扩容元素个数是直接翻倍;大于 1024 时,新容量是 1.25oldCap
- 扩容实际分配的内存:
扩容
- step1: 首先预估扩容为 newCap 后的元素个数
- step2: 计算 newCap 个元素需要多大 内存= 预估容量 x 元素类型大小
- step3: 匹配到合适的内存规格(8,16,32,48,96,112...),内存规格是由编程语言提前就从 OS 拿过来的
使用
package main
import (
"fmt"
)
func main() {
newStr()
shareStringArray()
}
func newStr() {
// new返回值是切片开始地址,但此时并未对切片分配内存,不能直接使用
ps := new([]string)
// (*ps)[0] = "eggo" // 并未分配地址,不能使用,运行时会报错 panic: runtime error: index out of range [0] with length 0
fmt.Printf("addr:%p \n", ps)
// append 为ps分配了空间
*ps = append(*ps, "eggo")
fmt.Printf("addr:%p \n", ps)
fmt.Println(ps)
}
func shareStringArray() {
arr := []int{0, 1, 2, 3, 4}
fmt.Printf("arr扩容前: addr:%p \n", arr)
fmt.Printf("arr: addr:%p ,value:%v ,len:%d, cap:%d \n", arr, arr, len(arr), cap(arr))
arr1 := arr[1:3]
fmt.Printf("arr1: addr:%p ,value:%v ,len:%d, cap:%d \n", arr1, arr1, len(arr1), cap(arr1))
arr2 := arr[4:]
arr2[0] = 5
// note1: 共享同一个底层数组,新截取的切片对底层数据修改会直接影响原始切片
fmt.Printf("value: arr:%v,arr2:%v\n", arr, arr2)
fmt.Printf("arr2: addr:%p ,value:%v ,len:%d, cap:%d \n", arr2, arr2, len(arr2), cap(arr2))
// note2:新切片若扩容,则会重新分配一段内存,然后将原始数据拷贝过来,
arr2 = append(arr2, 8, 9, 10)
fmt.Printf("arr扩容后: addr:%p \n", arr)
// note3:扩容后是新分配内存并没有共享以前的数组,对新切片修改不会影响原始切片
arr2[0] = 6
fmt.Printf("arr: addr:%p ,value:%v ,len:%d, cap:%d \n", arr, arr, len(arr), cap(arr))
fmt.Printf("arr2: addr:%p ,value:%v ,len:%d, cap:%d \n", arr2, arr2, len(arr2), cap(arr2))
}
-
切片使用前需要初始化分配内存(或者显示创建切片)
- 使用 new 创建切片只是返回切片起始地址,实际没有初始化
- 使用 make 创建切片时,初始化切片地址后会根据声明 len 和 cap 分配内存,若不指定大小,则 len 和 cap 一致
-
切片截取
- 截取切片后会独立为其分配一段内存,len 为截取后的实际元素个数,cap 为截取位置开始到原切片 cap 的差值
- 截取后的切片起始地址是基于原切片的偏移量计算的,实际上在扩容前,截取后的切片与原始切片是共享底层数组的
- 截取后切片扩容,则会独立分配一段内存空间,此时新切片和原始切片无关联,对新切片的更改不会对原切片造成影响
-
容量计算
- 切片声明时不指定 cap 大小,则 len 默认和 cap 大小一致
- 截取后切片的容量=原始切片容量- 切片截取位置处的索引
|
请发表评论