定义一个空切片_一文讲清楚 go 切片

415125f31b3eb071b5f854e551642794.png

go切片原型

// runtime/slice.go
type slice struct {
   array unsafe.Pointer// 引用底层数组的指针
   len   int// 引用数组元素的长度,使用下标对元素进行访问的时候,下标不能超过len 
   cap   int // 引用数组的容量,cap>=len
}

定义一个名为baseSlice 的切片

baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 

底层结构

835bee4b4a159c9a8818781db8cc5ea9.png

定义一个新的切片slice1

slice1 := baseSlice[2:5] //[]int{2,3,4} 

底层结构

61601e812328ad82ea12d2c95cce0ed0.png

定义一个新的切片slice2

slice2 := slice1[4:7] //[]int{6,7,8} 

底层结构

7e96220980fbeade15d808b05aec0bc0.png

源码

package main

import (
    "fmt"
)

func main() {
    baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    slice1 := baseSlice[2:5] //[]int{2,3,4}
    //这里slice1 cap是8,底层引用的数组为[]int{2, 3, 4, 5, 6, 7, 8, 9}
    //所以slice1 len=3, cap=8
    //由于 slice1 cap=8 所以slice1[4:7]并不会发生数组越界
    slice2 := slice1[4:7]                                                     //[]int{6,7,8}
    fmt.Printf("slice1:%v.len:%d.cap:%dn", slice1, len(slice1), cap(slice1)) //slice1:[2 3 4].len:3.cap:8
    fmt.Printf("slice2:%v.len:%d.cap:%dn", slice2, len(slice2), cap(slice2)) //slice2:[6 7 8].len:3.cap:4
}

切片是值传递

package main

import (
    "fmt"
)

func main() {
    baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    slice1 := baseSlice[2:5] //[]int{2,3,4}
    appendSlice(slice1)
    fmt.Printf("slice1:%vn", slice1)       //slice1:[2 3 4]
    fmt.Printf("baseSlice:%vn", baseSlice) //baseSlice:[0 1 2 3 4 10 6 7 8 9]
}

func appendSlice(s []int) { //值传递
    s = append(s, 10)                   //这里会改变底层数组里的值,如果append的元素个数大于底层引用数组的容量,则会触发一次数组copy
    fmt.Printf("append slice1:%vn", s) //append slice1:[2 3 4 10]
}

9aa06da5831cc3606210bcd69b09a903.png

引用传递的假象

如果在调用的方法里,修改了传入切片len内的值,导致底层数组对应的值被修改,从调用方法外看,就像传入的切片值被修改了。

package main

import (
    "fmt"
)

func main() {
    baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    slice1 := baseSlice[2:5] //[]int{2,3,4}
    appendSlice(slice1)
    fmt.Printf("slice1:%vn", slice1)       //slice1:[123 3 4],看起来是slice1被修改了,其实是由于底层数组被修改的缘故
    fmt.Printf("baseSlice:%vn", baseSlice) //baseSlice:[0 1 123 3 4 5 6 7 8 9]
}

func appendSlice(s []int) { //值传递
    // s = append(s, 10)
    s[0] = 123                                                          //这里会改变底层数组里的值
    fmt.Printf("append slice1:%v. len=%d. cap=%dn", s, len(s), cap(s)) //append slice1:[123 3 4]. len=3. cap=8
}

e5a05ad2237f08a386babc2953c860cc.png

深度copy

package main

import (
    "fmt"
)

func main() {
    baseSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    slice1 := baseSlice[4:7]          //[]int{6,7,8}
    fmt.Printf("slice1:%vn", slice1) //slice1:[4 5 6]
    //slice 深copy
    slice2 := make([]int, 3, 3)
    copy(slice2, slice1)
    fmt.Printf("slice2:%v. len=%d. cap=%dn", slice2, len(slice2), cap(slice2)) //slice2:[4 5 6]. len=3. cap=3
    slice2[0] = 100
    fmt.Printf("slice2:%vn", slice2)       //slice2:[100 5 6]
    fmt.Printf("slice1:%vn", slice1)       //slice1:[4 5 6]
    fmt.Printf("baseSlice:%vn", baseSlice) //baseSlice:[0 1 2 3 4 5 6 7 8 9]
}

6a1dee0d0a25212a963717d2ed6ddc01.png

扩容

// runtime/slice.go
//cap 为满足存放数据的最小容量
func growslice(et *_type, old slice, cap int) slice {
   ...
   if cap > doublecap {
      newcap = cap
   } else {
      if old.len < 1024 {//如果slice len<1024,扩容时容量增长2倍,也就是2个变4个
         newcap = doublecap
      } else {//如果slice len>=1024,扩容时增加原来容量的四分之一
         // Check 0 < newcap to detect overflow
         // and prevent an infinite loop.
         for 0 < newcap && newcap < cap {
            newcap += newcap / 4
         }
         ...
      }
   }

nil slice和empty slice

empty slice 底层数组指针指向实际存在的空数组的地址;

nil slice 底层数组指针指向的是nil;

package main

import (
    "fmt"
)

func main() {
    var nilSlice []int
    emptySlice := make([]int, 0)
    if emptySlice == nil {
        fmt.Println("emptySlice is nil.")
    }
    if nilSlice == nil {
        fmt.Println("nilSlice is nil.") //输出 nilSlice is nil.
    }
}

tips:

Go 语言的函数参数传递,只有值传递。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值