• Go 数组与切片


    #### Go 数组与切片
    ***渡风渡尘缘,修行亦修心***
    ##### 数组
    数组是可以存放多个同一类型的数据的集合,数组也是一种数据类型,它是值类型;
    数组如何定义?
    var 数组名 [数组大小]数据类型: var a [5]int
    package main
    
    import "fmt"
    
    func main(){
       var intArr [3]int
       intArr[0] = 10
       intArr[1] = 20
       intArr[2] = 30
       fmt.Println(intArr)
    }
    

      


    数组在内存中的布局:

      
    1. 数组的地址可以通过数组名获取: &intArr;
    2. 数组的第一个元素地址就是数组的地址:;
    3. 数组的各个元素的地址间隔根据数组的类型决定: int64-> 8 字节,int32-> 4字节;
    4. 数组如果未给元素赋值,元素的默认值为类型的零值: int64->0,string->"",float64->0;
    5. 访问数组中的元素: 使用下标索引方式(左闭右开方式),这个与其它语言一样,超出下标索引将会出错,索引越界;
    6. 数组的遍历有两种方式: for 传统方式; for range 方式 ;
    package main
    
    import "fmt"
    
    func main(){
       var a [5]int
       a[0] = 1
       a[1] = 2
       a[2] = 3
       // 默认值: a[3] = 0 , a[4] = 0
       // 下标越界: a[5] = 10  // 错误, 下标越界
       fmt.Println(a) //[1 2 3 0 0 ]
       fmt.Printf("a address is %p
    ",&a) // 0xc000078030
       fmt.Printf("a[0] address is %p
    ",&a[0]) //0xc000078030
       fmt.Printf("a[1] address is %p
    ",&a[1]) //0xc000078038
       fmt.Printf("a[2] address is %p
    ",&a[2]) //0xc000078040
       // 数组的遍历方式
       for i:=0;i<len(a);i++{
          fmt.Println(a[i])
       }
       // for range 方式 
       for k,v := range a {
          fmt.Println(a[k])
          fmt.Println(v)  // 这个值是元素的副本, 所以不建议在v 上操作: 取址或赋值;  
       }
    }
    

      


    ---
    数组有多种初始化方式:
    import "fmt"
    
    func main(){
       // 1. 字面量直接赋值
       var a [5]int = [5]int{1,2,3,4,5}
       var b = [5]int{6,7,8,9,0}
       // 2. 数组长度由编译器推导
       var c = [...]int{1,2,3,4,5,6}
       // 3. 根据索引位置直接赋值
       var d = [...]int{1:1,10:0,3:3}
       // 4. 类型推导
       f := [...]int{1:1,9:9,10:0}
       fmt.Println(a)
       fmt.Println(b)
       fmt.Println(c)
       fmt.Println(d)
       fmt.Println(f)
    }
    

      


    ---
    数组使用注意事项
    1. 数组是多个相同类型的集合,一旦声明或者定义了,数组的长度将是固定不变的,不能动态变化 ;
    2. 数组中的元素可以是任意类型,可以是值类型,也可以是引用类型,不可以混用;
    3. 数组创建后如果不赋值,将采用类型的零值 ;
    4. 数组属于值类型, 在默认情况下是传递数组是值传递,所以数组之间不会影响;
    5. 如果需要更改原数组,需要传递数组的地址(类似引用传递);
    6. 长度是数组类型的一部分,在传递参数时需要注意数组的长度;
    package main
    
    import "fmt"
    
    func test01(a [5]int){
       a[1] = 20
       fmt.Println(a)  //[1 20 3 4 5]
    }
    func test02(a *[5]int){
       a[1] = 20
       fmt.Println(*a) // [1 20 3 4 5]
    }
    func main(){
       var a = [5]int{1,2,3,4,5}
       test01(a)
       fmt.Println(a) // [1 2 3 4 5]
       // 如果需要修改原数组的值,需要传递数组的地址
       test02(&a)
       fmt.Println(a) // [1 20 3 4 5]
       // 传递参数时需要考虑数组的长度
       //var b = [3]int{1,2,3}
       //test01(b) // 错误, 数组的长度不正确
    }
    

      


    在Go 编程中,数组的使用还是不太多, 因为不能动态的扩容,数组一般作为已经确定的数据使用;
    ##### 切片
    1. 切片是数组的一个引用, 所以切片属于引用类型,在进行参数传递时,属于引用传递;
    2. 切片的使用与数组类似: 遍历,访问等;
    3. 切片的长度是可以动态变化的,所以也可以说切片是动态变化的数组;
    4. 切片的基本语法 :
    var 切片名 []类型
    package main
    
    import "fmt"
    
    func main(){
       // 这里只演示切片如何使用,稍后再介绍切片如何声明与赋值操作
       // 声明并定义一个数组
       var a = [5]int{1,2,3,4,5}
       // 切片s 引用已经存在的数组a
       var s = a[:] // 这种方式引用所有的数组元素,也可引用一部分数组元素 var s = a[1:3]
       fmt.Printf("%T
    ",s)
       fmt.Println(s)
       fmt.Println(len(s)) // 5 切片的长度
       fmt.Println(cap(s)) // 5 切片的容量,容量和长度是可以动态变化;
    }
    

      


    ---
    切片在内存中的布局

     
    1. slice 是一下引用类型;
    2. 从底层上讲,slice 实际上就是一个结构体(稍后会学习到)
    type slice struct {
    ptr *[2]int
    len int
    cap int
    }
    切片的初始化
    package main
    
    import "fmt"
    
    func main(){
       // 方式1 创建一个数组,让切片引用这个数组
       var a = [5]int{1,2,3,4,5}
       var s = a[:]
       // 同数组一样, 在赋值或取值时,仍然需要注意切片下标索引不可越界
       //s[10] = 0
       s[4] = 50
       fmt.Println(s)
       // 方式2
       // 切片是引用类型,所以声明后需要make 分配内存
       var b []int
       // make 分配内存,长度可以指定任意值,建议根据要存储的数据长度合适定义
       // make 参数,还可以定义切片的容量,如果定义了切片的容量,则要求容量大于切片的长度
       b = make([]int,5)
       fmt.Println(b)
       // 方式3
       // 字面量方式,声明后直接赋值
       var c = []int{1,2,3,4,5}
       fmt.Println(c)
    }
    

      


    1. 方式1: 直接引用已经存在的数组,开发者对底层是可见的;
    2. 方式2: 通过make 创建的切片,程序也会创建一个数组,不过这个数组对开发者不可见,由程序自己维护;
    3. 方式3: 类似make;
    ---
    切片使用的注意事项:
    1. 切片的遍历与访问同数组一样, 这里就不写了;
    2. 切片定义后,不能直接使用,需要引用一个数组或make 分配内存给切片使用;
    3. 使用append 函数可以对切片进行动态扩容;
    4. 使用copy 函数可以对切片进行拷贝,拷贝后的切片是独立的, 在新的切片上操作对原切片没有影响;
    5. 切片是引用类型,在给函数传递参数(切片)时,属于引用传递;
    package main
    
    import "fmt"
    
    func test01(a []int){
       a[0] = 100
       fmt.Println(a)
    }
    func main(){
       var a = []int{1,2,3}
       fmt.Println(a)
       // 使用append 函数将切片扩容
       a = append(a,4)
       fmt.Println(a)
       // 使用copy 函数进行切片复制
       // 在使用copy 函数时需要注意以下几点:
       //1. 目标(dst)必须是已经声明并初始化过的
       //2. 如果目标的长度小于被复制的切片长度,则以目标的长度为准复制,超出部分不复制;
       var b []int
       b = make([]int,3)// 超出元素4,将不会被复制
       copy(b,a)
       fmt.Println(b)
       // 对新切片的操作不会影响到原来的切片
       b[1] = 10
       fmt.Println(a)
       fmt.Println(b)
       // 切片属于引用传递
       test01(a)
       fmt.Println(a)
    }
    

      


    ---
    string 与slice
    在学习基本数据类型时我们学习了string 类型,知道它是由多个字节连接在一起的字符串;
    实际上string 底层是一个字节数组,所以string 也可以进行切片操作;
    但是string 是不可变的, 所以不能string[0]='x', 这种赋值操作; 如果需要更改,需要将string 转为[]byte 切片,更改完成后再转为string 类型;
    package main
    
    import "fmt"
    
    func main(){
       var a = "abcdef"
       // 可以进行切片处理
       fmt.Println(a[:3])
       // 不可以更改string 的值
       // a[1] = 'f' //这是错误的
       // 需要将string 转为 []byte 切片才可以操作
       var arr []byte
       arr = []byte(a)
       arr[1] = 'f'
       a = string(arr) // 更改完成后再将切片转为string 类型
       fmt.Println(a)
       // []byte 只能处理英文与数字,处理其它语言时需要将string 转为[]rune 切片
       var b = "你好啊"
       var arr2 []rune
       arr2 = []rune(b)
       arr2[0] = '我'
       b = string(arr2)
       fmt.Println(b)
    }
    

      个人微信公众号上有最新的文章,欢迎大家关注,一同交流学习

  • 相关阅读:
    树的可视化
    图的可视化
    1+1=2
    用xapian来做索引
    学习的快乐
    项目小结
    z=sin(xy)
    Min Stack
    互联网公司的文化
    为什么要读数学书
  • 原文地址:https://www.cnblogs.com/Mail-maomao/p/11411807.html
Copyright © 2020-2023  润新知