浅谈Go切片的值修改是否会覆盖数组的值 

2022-07-18,,,,

目录
  • 切片数组
    • 数组
  • 切片的值修改
    • 修改切片不覆盖数组的值
  • 切片的扩容机制
    • 切片不小于1024
  • 切片源码

    切片与数组

    数组

    数组是具有相同 唯一类型 的一组以编号且长度固定的数据项序列

    数组声明

    var identifier [len]type
    

    切片

    切片(slice)是对数组一个连续片段的引用,切片是一个引用类型,切片是一个指针。

    切片是一个长度可变的数组。

    切片声明

    var identifier []type
    

    切片初始化

    var slice1 []type = arr[start:end]
    

    切片的值修改

    修改切片的值覆盖数组的值

    代码

    package main
    
    import "fmt"
    
    func main() {
      arr := [5]int{1,2,3,4,5}
      fmt.printf("slice modification before: array=%v len=%d cap=%d\n", arr, len(arr), cap(arr))
      
      s := arr[0:3]
      fmt.printf("len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
      s = append(s, 6,10) 
      
      fmt.printf("len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
      fmt.printf("slice modification: array=%v len=%d cap=%d\n", arr, len(arr), cap(arr))
    }

    结果

    slice modification before: array=[1 2 3 4 5] len=5 cap=5
    len=3 cap=5 ptr=0xc00000c300 slice=[1 2 3]
    len=5 cap=5 ptr=0xc00000c300 slice=[1 2 3 6 10]
    slice modification: array=[1 2 3 6 10] len=5 cap=5

    由于未超出底层数组的容量,地址不变,数组还是原来的数组,所以修改切片会覆盖数组的值。

    修改切片不覆盖数组的值

    代码

    package main
    
    import "fmt"
    
    func main() {
      arr := [5]int{1,2,3,4,5}
      fmt.printf("slice modification before: array=%v len=%d cap=%d\n", arr, len(arr), cap(arr))
      
      s := arr[0:3]
      fmt.printf("len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
      s = append(s, 6,10,11) 
      
      fmt.printf("len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
      fmt.printf("slice modification: array=%v len=%d cap=%d\n", arr, len(arr), cap(arr))
    }

    结果

    slice modification before: array=[1 2 3 4 5] len=5 cap=5
    len=3 cap=5 ptr=0xc00000c300 slice=[1 2 3]
    len=6 cap=10 ptr=0xc0000141e0 slice=[1 2 3 6 10 11]
    slice modification: array=[1 2 3 4 5] len=5 cap=5

    超出底层数组的容量,地址变了,会分配一个新的数组,返回的切片指向这个新数组,旧的数组的值未被修改。

    切片的扩容机制

    切片小数1024

    代码

    package main
    
    import "fmt"
    
    func main() {
      arr := [5]int{1,2,3,4,5}
      
      s := arr[0:3]
      fmt.printf("len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
      s = append(s, 6,10,11) 
      
      fmt.printf("len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
    }

    结果

    before: len=3 cap=5 ptr=0xc00000c300 slice=[1 2 3]
    after: len=6 cap=10 ptr=0xc0000141e0 slice=[1 2 3 6 10 11]

    该切片的容量为源切片容量的2倍

    切片不小于1024

    代码

    package main
    
    import "fmt"
    
    func main() {
      arr := [1024]int{1,2,3,...,1024}
      s := arr[0:] 
      fmt.printf("before: len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
    
      s = append(s, 1025)
      fmt.printf("after: len=%d cap=%d ptr=%p slice=%v\n", len(s), cap(s), s, s)
    }

    结果

    before: len=1024 cap=1024 ptr=0xc000112000 slice=[1 2 3 ... 1024]
    after: len=1025 cap=1280 ptr=0xc00012c000 slice=[1 2 3 ... 1024 1025]

    切片容量在原来的切片的容量上增加了1/4

    切片源码

    如果切片的容量不够会调用growslice这个函数进行扩容

    //  go1.16.6 src/runtime/slice.go
    func growslice(et *_type, old slice, cap int) slice {
        ... // code
        newcap := old.cap
        doublecap := newcap + newcap
        if cap > doublecap {
            newcap = cap
        } else {
            if old.cap < 1024 {
                newcap = doublecap
            } else {
                // check 0 < newcap to detect overflow
                // and prevent an infinite loop.
                for 0 < newcap && newcap < cap {
                    newcap += newcap / 4
                }
                // set newcap to the requested cap when
                // the newcap calculation overflowed.
                if newcap <= 0 {
                    newcap = cap
                }
            }
        }
        // 根据切片类型和容量计算要分配内存的大小
        var overflow bool
        var lenmem, newlenmem, capmem uintptr
    
        switch {
        ... // code
        }
    
        ... // code
        // 将旧切片的数据搬到新切片开辟的地址中
        memmove(p, old.array, lenmem)
    
        return slice{p, old.len, newcap}
    }

    切片扩容的规则

    • 如果扩容之后,还没有触及原数组的容量,则切片中的指针指向的还是原数组,如果扩容后超过了原数组的容量,则开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。
    • 如果切片的容量小于 1024,则扩容时其容量大小乘以2;一旦容量大小超过 1024,则增长因子变成 1.25,即每次增加原来容量的四分之一。

     到此这篇关于浅谈go切片的值修改是否会覆盖数组的值 的文章就介绍到这了,更多相关go切片覆盖数组的值 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

    《浅谈Go切片的值修改是否会覆盖数组的值 .doc》

    下载本文的Word格式文档,以方便收藏与打印。