• 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时,还是要小心点,主要注意有无扩容这块。不然就会产生很多不可想像的问题了。

  • 相关阅读:
    UI5 Databind
    the meaning of myconputer environment path
    Linux查看日志常用命令
    fw: IP bonding in Linux.
    FW:expect tcl install
    install ET underlinux
    转expect 文一篇。
    linux 集萃
    try expect and autoexpect
    linux 压缩种类
  • 原文地址:https://www.cnblogs.com/smartrui/p/11358384.html
Copyright © 2020-2023  润新知