Adminxe's Blog | 低调求发展 - 潜心习安全 ,技术永无止境 | 谢谢您对本站的支持,有什么问题或者建议请及时联系:点击这里给我发消息

Go语言学习–切片(slice)

安全开发 MoCo, MoCo 1701℃ 0评论

引出

因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。 例如:

func arraySum(x [3]int) int{
    sum := 0
    for _, v := range x{
        sum = sum + v
    }
    return sum
}

这个求和函数只能接受[3]int类型,其他的都不支持。 再比如,

a := [3]int{1, 2, 3}

切片的本质

  1. 切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
  2. 切片的本质:切片就是一个框,框住了一块连续的内存。
  3. 切片属于引用类型,真正的数据都是保存再底层数据组里的

切片的基本定义与类型1

切片基本要素

// 切片指向了一个底层的数组。

// 切片的长度就是它元素的个数。

// 切片的容量是底层数组从切片的第一个元素到最后一个元素的数量。

切片的定义

var name []T

  • name:表示变量名
  • T:表示切片中的元素类型
 //切片的定义
    var s1 []int    //      定义一个存放int类型元素的切片
    var s2 []string //  定义一个存放string类型元素的切片
    fmt.Println(s1, s2)
    fmt.Println(s1 == nil) //nil:为空
    fmt.Println(s2 == nil)

初始化

//初始化
    s1 = []int{1, 2, 3}
    s2 = []string{"Adminxe", "Azjj", "化鱼"}
    fmt.Println(s1, s2)
    fmt.Println(s1 == nil)
    fmt.Println(s2 == nil)

长度和容量

// 切片的长度就是它元素的个数。

// 切片的容量是底层数组从切片的第一个元素到最后一个元素的数量。

 fmt.Printf("len(s1):%d cap(s1):%d\n", len(s1), cap(s1)) //len:长度
    fmt.Printf("len(s2):%d cap(s2):%d\n", len(s2), cap(s2)) //cap:容量

由数组得到切片

//2.由数组得到切片
    a1 := [...]int{1, 3, 5, 7, 9, 11, 13}
    s3 := a1[0:4] //  0:4  ==>  0、1、2    基于一个数组的切割,左包含右不包含(左开右闭)
    fmt.Println(s3)
    s4 := a1[1:6]
    fmt.Println(s4)
    s5 := a1[:4] //开始到4   ==> [0:4]
    s6 := a1[3:] //3到结束   ==> [3:len(a1)]    [7, 9, 11, 13]
    s7 := a1[:]  //开始到结束==> [0:len(a1)]
    fmt.Println(s5, s6, s7)
    //切片的容量是指底层数组的容量
    fmt.Printf("len(s5):%d cap(s5):%d\n", len(s5), cap(s5))
    //底层数组从切片的第一个元素到最后的元素数量
    fmt.Printf("len(s6):%d cap(s6):%d\n", len(s6), cap(s6))

切片再切割

//3.切片再切割
    s8 := s6[3:] //[1]
    fmt.Printf("len(s8):%d cap(s8):%d\n", len(s8), cap(s8))
    //切片是引用类型,都指向了底层的一个数组。
    fmt.Println("s6:", s6)
    a1[6] = 1300 //修改了底层数组的值
    fmt.Println("s6:", s6)
    fmt.Println("s8:", s8)

}

笔记练习代码1

package main

import "fmt"

// 切片指向了一个底层的数组。
// 切片的长度就是它元素的个数。
// 切片的容量是底层数组从切片的第一个元素到最后一个元素的数量。

func main() {
    //切片的定义
    var s1 []int    //      定义一个存放int类型元素的切片
    var s2 []string //  定义一个存放string类型元素的切片
    fmt.Println(s1, s2)
    fmt.Println(s1 == nil) //nil:为空
    fmt.Println(s2 == nil)
    //初始化
    s1 = []int{1, 2, 3}
    s2 = []string{"Adminxe", "Azjj", "化鱼"}
    fmt.Println(s1, s2)
    fmt.Println(s1 == nil)
    fmt.Println(s2 == nil)
    //长度和容量
    fmt.Printf("len(s1):%d cap(s1):%d\n", len(s1), cap(s1)) //len:长度
    fmt.Printf("len(s2):%d cap(s2):%d\n", len(s2), cap(s2)) //cap:容量

    //2.由数组得到切片
    a1 := [...]int{1, 3, 5, 7, 9, 11, 13}
    s3 := a1[0:4] //  0:4  ==>  0、1、2    基于一个数组的切割,左包含右不包含(左开右闭)
    fmt.Println(s3)
    s4 := a1[1:6]
    fmt.Println(s4)
    s5 := a1[:4] //开始到4   ==> [0:4]
    s6 := a1[3:] //3到结束   ==> [3:len(a1)]    [7, 9, 11, 13]
    s7 := a1[:]  //开始到结束==> [0:len(a1)]
    fmt.Println(s5, s6, s7)
    //切片的容量是指底层数组的容量
    fmt.Printf("len(s5):%d cap(s5):%d\n", len(s5), cap(s5))
    //底层数组从切片的第一个元素到最后的元素数量
    fmt.Printf("len(s6):%d cap(s6):%d\n", len(s6), cap(s6))
    //3.切片再切割
    s8 := s6[3:] //[1]
    fmt.Printf("len(s8):%d cap(s8):%d\n", len(s8), cap(s8))
    //切片是引用类型,都指向了底层的一个数组。
    fmt.Println("s6:", s6)
    a1[6] = 1300 //修改了底层数组的值
    fmt.Println("s6:", s6)
    fmt.Println("s8:", s8)

}

切片的基本定义与类型2

make()函数创造切片

s1 := make([]int, 5, 10) // (类型,长度,容量(容量要是不写则延续长度))
    fmt.Printf("s1=%v len(s1)-%d cap(s1)-%d\n", s1, len(s1), cap(s1))

    s2 := make([]int, 0, 10) // (类型,长度,容量)
    fmt.Printf("s2=%v len(s2)-%d cap(s2)-%d\n", s2, len(s2), cap(s2))

判断切片是否为空

//要检查切片是否为空,使用len(s) == 0来判断,不能使用s == nil来判断。

切片不能直接比较

    //片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。

    // 切片唯一合法的比较操作是和nil比较。

    // 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。

//但是我们不能说一个长度和容量都是0的切片一定是nil

var s11 []int         //len(s1)=0;cap(s1)=0;s1==nil
    s22 := []int{}        //len(s2)=0;cap(s2)=0;s2!=nil
    s33 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil

切片的赋值拷贝

s3 := []int{1, 3, 5}
    s4 := s3
    s3[0] = 1000
    fmt.Println(s3, s4)

切片的遍历

  //1.索引遍历
    for i := 0; i < len(s3); i++ {
        fmt.Println(s3[i])

    }
    //2.for range循环
    for i, v := range s3 {
        fmt.Println(i, v)
    }

}

笔记练习代码2

package main

//切片的本质:切片就是一个框,框住了一块连续的内存。
//切片属于引用类型,真正的数据都是保存再底层数据组里的

import "fmt"

//make()函数创造切片
func main() {
    s1 := make([]int, 5, 10) // (类型,长度,容量(容量要是不写则延续长度))
    fmt.Printf("s1=%v len(s1)-%d cap(s1)-%d\n", s1, len(s1), cap(s1))

    s2 := make([]int, 0, 10) // (类型,长度,容量)
    fmt.Printf("s2=%v len(s2)-%d cap(s2)-%d\n", s2, len(s2), cap(s2))

    //要检查切片是否为空,使用len(s) == 0来判断,不能使用s == nil来判断。

    //切片不能直接比较
    //片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。
    // 切片唯一合法的比较操作是和nil比较。
    // 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。
    //但是我们不能说一个长度和容量都是0的切片一定是nil
    var s11 []int         //len(s1)=0;cap(s1)=0;s1==nil
    s22 := []int{}        //len(s2)=0;cap(s2)=0;s2!=nil
    s33 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
    fmt.Printf("len(s11):%d cap(s11):%d\n", len(s11), cap(s11))
    fmt.Printf("len(s22):%d cap(s22):%d\n", len(s22), cap(s22))
    fmt.Printf("len(s33):%d cap(s33):%d\n", len(s33), cap(s33))
    //切片的赋值拷贝
    s3 := []int{1, 3, 5}
    s4 := s3
    s3[0] = 1000
    fmt.Println(s3, s4)
    //切片的遍历
    //1.索引遍历
    for i := 0; i < len(s3); i++ {
        fmt.Println(s3[i])

    }
    //2.for range循环
    for i, v := range s3 {
        fmt.Println(i, v)
    }

}

append()方法为切片追加元素

package main

import "fmt"

//append()为切片追加元素

func main() {
    s1 := []string{"Adminxe", "Azjj", "化鱼"}
    fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
    // s1[3] = "广州" //错误的写法  会导致编译错误:索引越界
    // fmt.Println(s1)

    //调用append函数必须用原来的切片变量来接收返回值
s1 = append(s1, "MoCo") //当append追加元素,原来的底层数组放不下的时候,Go底层就会把底层数组换一个
//如果源切片容量够,就修改底层数组;若容量不够则开辟新的内存空间,不影响数组
//append相当于新建了一个数组,然后对原数组进行赋值操作
    fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
    s1 = append(s1, "海底", "哦哦哦") // 必须用变量接收append的返回值
    fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
    ss := []string{"瓜皮辰", "Corl7", "Fengxin"}
    s1 = append(s1, ss...) // ...表示拆开
    fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

}

Copy()函数复制切片

package main

import "fmt"

func main() {
    a1 := []int{1, 3, 5}
    a2 := a1 //赋值
    var a3 = make([]int, 3, 3)
    copy(a3, a1) // copy   copy从新开辟了一片空间,等同于建立副本
    fmt.Println(a1, a2, a3)
    a1[0] = 100
    fmt.Println(a1, a2, a3)

}

从切片中删除元素

package main

import "fmt"

func main() {
    a1 := []int{1, 3, 5}
        
    //从切片中删除元素
    //将a1中的索引为1的3这个元素删掉

    a1 = append(a1[:1], a1[2:]...)
    fmt.Println(a1)
    fmt.Println(cap(a1))
    fmt.Println(a1)
    //将a1中的索引为1的3这个元素删掉专项解释
    x1 := [...]int{1, 3, 5} //数组
    s1 := x1[:]             //切片
    fmt.Println(s1, len(s1), cap(s1))
    //1.切片不保存具体的值
    //2.切片对应一个底层数组
    //3.底层数组都是占用一块连续的内存
    fmt.Printf("%p\n", &s1[0])     //%p:内存地址
    s1 = append(s1[:1], s1[2:]...) //修改了底层数组
    fmt.Printf("%p\n", &s1[0])
    fmt.Println(s1, len(s1), cap(s1))
    //?
    s1[0] = 100     //修改底层数组
    fmt.Println(x1) // [1 5 5]

}

参考链接

https://www.liwenzhou.com/posts/Go/06_slice/

转载请注明:Adminxe's Blog » Go语言学习–切片(slice)

喜欢 (172)or分享 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址