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\n",&a) // 0xc000078030
   fmt.Printf("a[0] address is %p\n",&a[0]) //0xc000078030
   fmt.Printf("a[1] address is %p\n",&a[1]) //0xc000078038
   fmt.Printf("a[2] address is %p\n",&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\n",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)
}

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

posted @ 2019-08-26 12:17  mail_maomao  阅读(153)  评论(0编辑  收藏  举报