zoukankan      html  css  js  c++  java
  • Go探索-Slice

    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
    • 扩容实际分配的内存:
      • 内存= 预估容量 x 元素类型大小

    扩容

    • 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 
    ", ps)
    	// append 为ps分配了空间
    	*ps = append(*ps, "eggo")
    
    	fmt.Printf("addr:%p 
    ", ps)
    	fmt.Println(ps)
    }
    
    func shareStringArray() {
    	arr := []int{0, 1, 2, 3, 4}
    	fmt.Printf("arr扩容前: addr:%p 
    ", arr)
    	fmt.Printf("arr: addr:%p ,value:%v ,len:%d, cap:%d 
    ", arr, arr, len(arr), cap(arr))
    	arr1 := arr[1:3]
    	fmt.Printf("arr1: addr:%p ,value:%v ,len:%d, cap:%d 
    ", arr1, arr1, len(arr1), cap(arr1))
    	arr2 := arr[4:]
    	arr2[0] = 5
    	// note1: 共享同一个底层数组,新截取的切片对底层数据修改会直接影响原始切片
    	fmt.Printf("value: arr:%v,arr2:%v
    ", arr, arr2)
    	fmt.Printf("arr2: addr:%p ,value:%v ,len:%d, cap:%d 
    ", arr2, arr2, len(arr2), cap(arr2))
    
    	// note2:新切片若扩容,则会重新分配一段内存,然后将原始数据拷贝过来,
    	arr2 = append(arr2, 8, 9, 10)
    	fmt.Printf("arr扩容后: addr:%p 
    ", arr)
    
    	// note3:扩容后是新分配内存并没有共享以前的数组,对新切片修改不会影响原始切片
    	arr2[0] = 6
    	fmt.Printf("arr: addr:%p ,value:%v ,len:%d, cap:%d 
    ", arr, arr, len(arr), cap(arr))
    	fmt.Printf("arr2: addr:%p ,value:%v ,len:%d, cap:%d 
    ", arr2, arr2, len(arr2), cap(arr2))
    }
    
    1. 切片使用前需要初始化分配内存(或者显示创建切片)

      • 使用 new 创建切片只是返回切片起始地址,实际没有初始化
      • 使用 make 创建切片时,初始化切片地址后会根据声明 len 和 cap 分配内存,若不指定大小,则 len 和 cap 一致
    2. 切片截取

      • 截取切片后会独立为其分配一段内存,len 为截取后的实际元素个数,cap 为截取位置开始到原切片 cap 的差值
      • 截取后的切片起始地址是基于原切片的偏移量计算的,实际上在扩容前,截取后的切片与原始切片是共享底层数组的
      • 截取后切片扩容,则会独立分配一段内存空间,此时新切片和原始切片无关联,对新切片的更改不会对原切片造成影响
    3. 容量计算

      • 切片声明时不指定 cap 大小,则 len 默认和 cap 大小一致
      • 截取后切片的容量=原始切片容量- 切片截取位置处的索引
    严律己、宽待人
  • 相关阅读:
    51nod1363-最小公倍数之和
    [模板] 数论题的一些经验
    WC2019游记 && 课件
    (伪)WC2019题解
    [模板] 后缀自动机&&后缀树
    [模板] 二分图博弈 && BZOJ2463:[中山市选2009]谁能赢呢?
    界面修改日志
    [模板] dp套dp && bzoj5336: [TJOI2018]party
    BZOJ1025:[SCOI2009]游戏
    [模板] BSGS/扩展BSGS
  • 原文地址:https://www.cnblogs.com/luckyCoder/p/14869860.html
Copyright © 2011-2022 走看看