golang slice

golang slice
1、slice是什么


   slice译作“切片”。它非常类似数组,通过下标进行访问。我们都知道slice是建立在数组上的,操作起来比数组更灵活

2、slice源码 (runtime/slice.go)


type slice struct {
    array unsafe.Pointer  // 元素指针
    len   int  // 长度
    cap   int  // 容量
}
array 就是指向下层数组的指针
len 长度  切片访问元素时,下标不能超过len大小  len长度最大长度也就等于容量
cap 容量  也是slice能扩张到的最大限度。

3、slice的创建


   1、直接声明
      var s1 []int
   2、new
      s2 := *new([]int)
   3、字面量
      s3 := []int{1,3,5,7}
   4、make
      s4 := make([]int,5,11)
   5、从切片或者数组截取
      s5 := s4[1:]


4、直接声明


   一定注意,此时声明的切片其实是nil切片。它的长度和容量都是0.
   验证:
      var s1  []int
   println(s1 == nil)
   此时打印结果为: true

   另外还有一种切片叫空切片,empty slice,即长度和容量也都为0,但是其数组指向地址是 0xc42003bda0
   empty slice和nil的对比结果为false

   var s1  []int
   var s2 = []int{}
   println(s1 == nil) // 打印结果为:true
   println(s2==nil)   // 打印结果为: false

官方推荐使用nil切片

5、字面量


      var s3 = []int{1,3,5,7,10:98}  // 注意10:98表示下标为10 的元素赋值是98
   fmt.Println(s3)
   // 打印结果是 [1 3 5 7 0 0 0 0 0 0 98]


6、make方式


   需要传入类型,长度,容量
   s5 := make([]int, 5, 8)
   s5[4]=10
   fmt.Println(s5[4])


7、从切片或者数组截取


   s5 := make([]int, 5, 8)
   s6:=s5[:3]
   s6[0]=11  // 此时s5 s6共用底层的数组,s5改变了数组的0号索引的数据,
            // 所以切片s6获取0号索引得到的数据不再是0,而是11了

   fmt.Println(s5[0])// 打印结果 11
   fmt.Println(s6[0])// 打印结果 11

8、make和new的拉扯

make()比new()函数多一些操作,new()函数只会进行内存分配并做默认的赋0初始化,而make()可以先为底层数组分配好内存,然后从这个底层数组中再额外生成一个slice并初始化。另外,make只能构建slice、map和channel这3种结构的数据对象,因为它们都指向底层数据结构,都需要先为底层数据结构分配好内存并初始化。

每一个slice结构都由3部分组成:容量(capacity)、长度(length)和指向底层数组某元素的指针,它们各占8字节(1个机器字长,64位机器上一个机器字长为64bit,共8字节大小,32位架构则是32bit,占用4字节),所以任何一个slice都是24字节(3个机器字长)。

Pointer:表示该slice结构从底层数组的哪一个元素开始,该指针指向该元素
Capacity:即底层数组的长度,表示这个slice目前最多能扩展到这么长
Length:表示slice当前的长度,如果追加元素,长度不够时会扩展,最大扩展到Capacity的长度(不完全准确,后面数组自动扩展时解释),所以Length必须不能比Capacity更大,否则会报错


网上很多种解读,但是最终要的一点是:
    有两个切片,s2是s1的衍生,但随着s2不断append,如果你append 的数据超过了slice的容量(capacity).那么必然会导致一个新的slice生成,两次之后二者就“脱节”了,之后对一个的元素改动就不能同步到另一个了。此时的slice地址id和原来那个slice不一样。
    

 

package main

import "fmt"

func main() {
	//orig := make([]int, 4, 7)
	orig := []int{1,2,3,4}
	fmt.Println("processIt before -->", orig, "cap is ", cap(orig))
	ret := processIt(orig)
	//processIt(orig)
	fmt.Println("processIt after ret-->", ret)
	fmt.Println("processIt after orig -->", orig)
}

func processIt(l []int) []int{
	fmt.Println("processIt start -->", l)
	l[0]=888
	//l[0]=100
	l = append(l, 100,101,102)

	l[6]=999

	fmt.Println("processIt end -->", l)
	return l
}



输出结果:

processIt before --> [1 2 3 4] cap is  4
processIt start --> [1 2 3 4]
processIt end --> [888 2 3 4 100 101 999]
processIt after ret--> [888 2 3 4 100 101 999]
processIt after orig --> [888 2 3 4]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值