zoukankan      html  css  js  c++  java
  • go 切片和数组

    一、区别

      数组的长度是固定的,初始化后就不能修改长度,大家平时代码中比较少用。

      slice是对数组的一个封装,可以动态扩容,slice是一个结构体,包含三个字段:底层数组、长度、容量

     二、初始化方式

      数组

    var a = [4]int{1,2,3,4}
    var b = [...]int{1,2,3,4,5}

      切片

    var a []int //当前值为nil,未分配内存空间
    var b = []int{1, 2, 3}
    var c = make([]int, 5) //初始化5个元素,默认值为0,容量为5,在后面追加元素就会触发扩容
    var d = make([]int, 0, 5) //初始化0个元素,容量为5

    注意:slice在追加元素时如果容量不够会触发扩容,原slice拷贝到新slice中,扩容规则网上的说法:原slice小于1024,新的slice容量变成原slice的2倍,大于1024时,新slice容量变成原slice的1.25倍。这里的说法只是一个大概不是很准确,感兴趣的朋友可以查看网络上相关文章或源码,其实这对于我们平时开发影响不是很大,想要最求性能可以在初始化时预估容量,减少扩容和拷贝的次数。

    三、拷贝

      第一种

    var a = []int{1,2,3,4,5}
    var b = a[2:4]  //拷贝a中部分数据到b中
    
    b[0] = 12 //改变b第一个元素的值
    log.Println(a,b) //输出:[1 2 12 4 5] [12 4] 两个数组的值都被改变了,说明数据是地址拷贝
    
    b = append(b,6,7,8,9) //追加元素,容量不够引发扩容
    b[0] = 13 //改变b第一个元素的值
    log.Println(a,b) //输出:[1 2 12 4 5] [13 4 6 7 8 9] b中的数据改变了,a中的值并没有改变,扩容以后b和a中的元素已经没有了关系

      第二种

    var a = []int{1,2,3,4,5}
    var b = make([]int,5)
    
    copy(b,a) //值拷贝
    b[0] = 12
    log.Println(a,b) //输出:[1 2 3 4 5] [12 2 3 4 5] 

    四、平时使用需要注意的地方

      1、作为函数参数传递(go语言的函数参数传递,只有值传递,没有引用传递),但有时候我们会产生一些误区,看看下面这个例子

    func f(a []int){
        a[0] = 12
        a = append(a,6)
    }
    
    func main(){
        var a = []int{1,2,3,4,5}
        f(a)
        log.Println(a) //输出:[12 2 3 4 5] 第一个元素改变了,但值6并没有追加到a中
    }

       2、range 中改变值

    var a = []int{1, 2, 3, 4, 5}
    
    for _, v := range a { //不会改变a中元素的值(v只是一个临时变量)
        v = v + 2
    }
    log.Println(a) //输出:[1 2 3 4 5]
    
    for i := range a { //会改变a中的值
        a[i] = i + 2
    }
    log.Println(a) //输出:[2 3 4 5 6]

    五、扩展

      1、slice的结构

    func main(){
        var a = []int{1, 2, 3, 4, 5}
        s := (*SliceHeader)(unsafe.Pointer(&a))
    
        log.Println(s.Len,s.Cap) //输出:5 5
    }
    
    type SliceHeader struct {
        Data uintptr    //底层数组地址
        Len  int        //长度
        Cap  int        //容量
    }

      2、append方法必须要有接收者

    var a = []int{1, 2, 3, 4, 5}
    append(a,6) //编译失败

      3、数组的元素在内存中地址是连续的

    var a = []int{1, 2, 3, 4, 5}
    s := (*reflect.SliceHeader)(unsafe.Pointer(&a))
    
    //根据数组索引获取元素值
    arrGet := func(n int)int{
        if n >= s.Len{
            panic("索引越界")
        }
        v := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(s.Data))+unsafe.Sizeof(int(0))*uintptr(n)))
        return *v
    }
    log.Println(arrGet(0)) //输出:1
    log.Println(arrGet(1)) //输出:2
    log.Println(arrGet(5)) //输出:panic: 索引越界

    以上内容为个人理解,如有问题欢迎指出。

  • 相关阅读:
    Javascript FP-ramdajs
    微信小程序开发
    SPA for HTML5
    One Liners to Impress Your Friends
    Sass (Syntactically Awesome StyleSheets)
    iOS App Icon Template 5.0
    React Native Life Cycle and Communication
    Meteor framework
    RESTful Mongodb
    Server-sent Events
  • 原文地址:https://www.cnblogs.com/fanxp/p/13769350.html
Copyright © 2011-2022 走看看