当前位置:   article > 正文

Golang 理解slice很重要_go make 0 10

go make 0 10

Go语言的slice结合了数组的性能优势,同时提供了便捷的操作模式。它的本质只是在数组之上提供了访问指定范围内(起始索引和终止索引)的数组数据。要理解slice首先要说说数组。任何数组 A 都具有一个固定不变的长度 len 和指向首地址的指针 ptr,以及高效访问第n个数组成员的方法 A[ptr+n] (0 <= x < N)。
这里写图片描述
数组的长度是固定的,这是数组的最大劣势。在实际应用中大部分情况下我们都期望数组的长度是可以变动的。比如变小取其片段,变大追加新成员。变小的话只需一个新数组首地址 ptr和长度len 即可,变大的话就必须要重新申请一块更大的内存空间。至此golang屏蔽了原版数组,为我们提供尽可能更方便的数组,这就是slice。slice具有ptr首地址指针和len属性,增加了capacity属性(隐含原版数组len )。我们可以把slice看成一个原版数组上的虚拟子数组。
这里写图片描述
不仅如此我们甚至可以根据以上内存布局直接把slice转化为结构体。

type Slice struct {
    ptr unsafe.Pointer
    len int           
    cap int           
}

func main() {
    s1 := []int{0}   
    s2 := (*Slice)(unsafe.Pointer(&s1))
    fmt.Printf("ptr:%v len:%v cap:%v", s2.ptr, s2.len, s2.cap) 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

slice总是声明时的原版数组的子集片段(非 真子集)。可以节省大量的数组拷贝行为,当然也是潜在错误的根源。

    p := make([]byte, 0, 10) //capacity为10,即申请的原数组内存长度为10
    fmt.Println(len(p))      //slice为原数组子集,长度为0

    p = p[10:11]             //panic: 超出原数组范围报错

    s := p[9:10]             //可以在原数组的长度内任意取片段
    s[0] = 9                 
    fmt.Println(s[0])        //9

    a := p[:cap(p)]          //原数组的全集
    a[9] = 5                 //s[0]被修改

    fmt.Println(s[0])        //5
    fmt.Println(a[9])        //5

    //原数组子集slice可当成独立变量使用
    p = append(p,'t','o','m','b','o','y')
    name := p[0:3]
    sex := p[3:6]
    fmt.Println(string(name)) //输出 tom
    fmt.Println(string(sex))  //输出 boy

    //当然应尽量不要去append
    name = append(name,'g','i','r','l')
    fmt.Println(string(sex))  //输出 gir
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

以上是操作数组和它的子集,golang提供的方式还是很方便的。但一定要注意修改slice成员值的覆盖问题,毕竟同一个原数组的各个子集是共用内存空间的。
当原数组长度不够时,我们追加成员将导致重新申请内存。这是slice的另一个大坑。

    //紧跟上面的示例

    s[0] = 9 
    fmt.Println(s[0]) //9 
    fmt.Println(a[9]) //9 s与a处于同一原数组中

    x := s
    s = append(s, 10) //长度不够,需要新申请内存空间
    fmt.Println(cap(s)) //新申请的内存空间长度为8
    s[0] = 0

    fmt.Println(s[0]) //0
    fmt.Println(x[0]) //9 x与a处于同一原数组中
    fmt.Println(a[9]) //9 s与a处于不同数组中
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

从上面例子可以看出我们应该尽量避免对真子集slice进行append操作,因为这极可能偏离开发者的意图。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/206459
推荐阅读
相关标签
  

闽ICP备14008679号