zoukankan      html  css  js  c++  java
  • golang中,slice的几个易混淆点

    slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀。不过,我感觉这个东西用的不好坑太多了。还是需要了解下他底层的实现

    slice的结构定义

    type slice struct {
    	array unsafe.Pointer
    	len   int
    	cap   int
    }
    

    看结构定义,就三个字段,那个指针指向的就是底层数组,所以说slice的底层结构就是数组。

    slice的声明

    第一种方式
    var s []int #和数组差不多,[]中间不要数字
    第二种方式
    s :=[]int{}
    第三种方式
    s :=make([]int,len,cap)
    len 表示元素的实际数量
    cap 表示可以容纳元素的最大个数
    

    以上三种定义是有区别的,第一种定义的s值为nil slice,其它两种为empty slice.要编码时要加以区分

    slice扩容逻辑

    1. 如果slice容量足够容纳新增的元素,那么不会扩容。
    2. 如果新增元素后容量不足,则会扩容为原容量的 2 倍大小,如果原 slice 容量大于1024,那扩容后的容量等于每次扩容前增加 1/4。同时将原来的数据拷贝到新的数组中,所以还是要合理使用扩容。

    slice的几个混淆点

    一、slice作参数传递

    slice作参数传递时,虽然是值传递(拷贝一份),但是slice的内部指针,指向的还是同一个数组是引用,所以会有影响。

    func main() {
    	var s []int
    	for i := 1; i <= 4; i++ {
    		s = append(s, i)
    	}
    	fmt.Println("原始s的值",s)
    	changeslice(s)
    	fmt.Println("s被改变后的值", s)
    
    }
    
    func changeslice(s []int) {
       s[0] = 7
       s[1] = 8
       fmt.Println("changeslice后的s值", s)
    }
    
    输出:
    原始s的值 [1 2 3 4]
    changeslice后的s值 [7 8 3 4]
    s被改变后的值 [7 8 3 4]
    

    二、使用append,无扩容

    继续上面的,这次在函数里面加上append;
    append后,生成的新的slice。但是因为没有扩容,指向的还是同一个数组,所以还是会影响到原来的slice

    func main() {
    	var s []int
    	for i := 1; i <= 3; i++ {
    		s = append(s, i)
    	}
    
    	fmt.Println("原始s的值",s)
    	changeslice(s)
    	fmt.Println("s被改变后的值", s)
    
    }
    
    func changeslice(s []int) {
    
       s = append(s,777)
       s[1] = 999
       fmt.Println("changeslice后的s值", s)
    
    }
    输出:
    原始s的值 [1 2 3]
    changeslice后的s值 [1 999 3 777]
    s被改变后的值 [1 999 3]
    

    三、继续append,有扩容

    这次继续使用append,不过这次多appen几个元素,
    发现如果扩容后,那么生成的新slice就不会影响到原来的了,因为
    指向的不是原来的数组

    func main() {
    	var s []int
    	for i := 1; i <= 3; i++ {
    		s = append(s, i)
    	}
    
    	fmt.Println("原始s的值",s)
    	changeslice(s)
    	fmt.Println("s被改变后的值", s)
    
    }
    
    func changeslice(s []int) {
    
       s = append(s,777,888)
       s[1] = 999
       fmt.Println("changeslice后的s值", s)
    
    }
    输出:
    原始s的值 [1 2 3]
    changeslice后的s值 [1 999 3 777 888]
    s被改变后的值 [1 2 3]
    
    

    通过slice切割生成slice

    从slice切割生成的slice,其实还是指向的同一个底层数组,一方有改动,还是会影响到对方。

       var s []int
       s = []int{1,2,3,4,5,6}
       fmt.Println("原始的s的值", s)
       s2 :=s[1:5]
       fmt.Println("切割后的:s2", s2)
       s2[0] = 99
       fmt.Println("s2重新赋值:",s2)
       fmt.Println("s的值:", s)
       s3 :=s2[0:2]
       fmt.Println("s3的值", s3)
       s3[1] = 99999
       fmt.Println("s2的值",s2)
       fmt.Println("s的值",s)
    
    输出:
    原始的s的值 [1 2 3 4 5 6]
    切割后的:s2 [2 3 4 5]
    s2重新赋值: [99 3 4 5]
    s的值: [1 99 3 4 5 6]
    s3的值 [99 3]
    s2的值 [99 99999 4 5]
    s的值 [1 99 99999 4 5 6]
    

    总结

    平时使用slice时,还是要小心点,主要注意有无扩容这块。不然就会产生很多不可想像的问题了。

  • 相关阅读:
    教程:在 Visual Studio 中开始使用 Flask Web 框架
    教程:Visual Studio 中的 Django Web 框架入门
    vs2017下发现解决python运行出现‘No module named "XXX""的解决办法
    《sqlite权威指南》读书笔记 (一)
    SQL Server手工插入标识列
    hdu 3729 I'm Telling the Truth 二分图匹配
    HDU 3065 AC自动机 裸题
    hdu 3720 Arranging Your Team 枚举
    virtualbox 虚拟3台虚拟机搭建hadoop集群
    sqlserver 数据行统计,秒查语句
  • 原文地址:https://www.cnblogs.com/smartrui/p/11358384.html
Copyright © 2011-2022 走看看