go切片的概念及用法

这篇文章主要介绍“go切片的概念及用法”,在日常操作中,相信很多人在go切片的概念及用法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go切片的概念及用法”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

创新互联拥有一支富有激情的企业网站制作团队,在互联网网站建设行业深耕10年,专业且经验丰富。10年网站优化营销经验,我们已为上千家中小企业提供了网站制作、成都网站设计解决方案,按需网站开发,设计满意,售后服务无忧。所有客户皆提供一年免费网站维护!

go切片原型

// runtime/slice.gotype 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}

底层结构

go切片的概念及用法

定义一个新的切片slice1

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

底层结构

定义一个新的切片slice2

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

底层结构

go切片的概念及用法

源码

package mainimport ("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:%d\n", slice1, len(slice1), cap(slice1)) //slice1:[2 3 4].len:3.cap:8    fmt.Printf("slice2:%v.len:%d.cap:%d\n", slice2, len(slice2), cap(slice2)) //slice2:[6 7 8].len:3.cap:4}

切片是值传递

package mainimport ("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:%v\n", slice1)       //slice1:[2 3 4]    fmt.Printf("baseSlice:%v\n", 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:%v\n", s) //append slice1:[2 3 4 10]}

go切片的概念及用法

引用传递的假象

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

package mainimport ("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:%v\n", slice1)       //slice1:[123 3 4],看起来是slice1被修改了,其实是由于底层数组被修改的缘故    fmt.Printf("baseSlice:%v\n", 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=%d\n", s, len(s), cap(s)) //append slice1:[123 3 4]. len=3. cap=8}

go切片的概念及用法

深度copy

package mainimport ("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:%v\n", slice1) //slice1:[4 5 6]    //slice 深copy    slice2 := make([]int, 3, 3)copy(slice2, slice1)fmt.Printf("slice2:%v. len=%d. cap=%d\n", slice2, len(slice2), cap(slice2)) //slice2:[4 5 6]. len=3. cap=3    slice2[0] = 100fmt.Printf("slice2:%v\n", slice2)       //slice2:[100 5 6]    fmt.Printf("slice1:%v\n", slice1)       //slice1:[4 5 6]    fmt.Printf("baseSlice:%v\n", baseSlice) //baseSlice:[0 1 2 3 4 5 6 7 8 9]}

go切片的概念及用法

扩容

// 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 语言的函数参数传递,只有值传递。

到此,关于“go切片的概念及用法”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!


网页标题:go切片的概念及用法
转载来源:http://scyanting.com/article/jishdp.html