Golang 理解slice很重要

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) 
}

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

以上是操作数组和它的子集,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处于不同数组中

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值