#### 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) }
个人微信公众号上有最新的文章,欢迎大家关注,一同交流学习