13、做中学 | 初一下期 Golang数组与切片
每当一个新事物出现的时候,我们要先接近、认识、模仿、练习、反思、到最后的实战
一、接近数组与切片
1. 看过西游记吗?
在生活中或者小说故事中,我们总能看到团伙、帮派,在编程里也有同样的概念,数组:同一种数据类型的多个值为数组。
就是同一伙人(数据类型),算是一个数组,不能有其它团伙(数据类型)在自己团队卧底!
比如:我们看的西游记师徒取经为一个团队(数据类型为名称),他们就可以看作一个数组:
package mainimport "fmt"func main() {// 西游记师徒五人sunWuKong := "孙悟空"zhuBaJie := "猪八戒"shaHeShang := "沙和尚"tangSen := "唐僧"baiLongMa := "白龙马"// 组成了一个团队==数组team := [5]string{sunWuKong, zhuBaJie, shaHeShang, tangSen, baiLongMa}fmt.Println("西游记取经团队:", team)// 西游记取经团队: [孙悟空 猪八戒 沙和尚 唐僧 白龙马]}
2. 军训过吗?
在军训过程中,还记得怎么报数吗?从左到右,从右到左,我们报的数是什么意思,答案是位置(这个很关键,要记住)。
如果教官要让第3个人出列,之前报过3的就会上前一步;如果教官要让报过2-5的人出列,那之前报过2-5的人就会上前一步;这些口令就是切片!
口令(切片):报数2-5上前一步(切片内容)!
二、数组
了解了一组同类型的数据封装为数组,那如何声明,如何使用呢?
1. 声明数组
声明数组的格式如下
var 数组名称 [数组大小]数据类型// 第一种声明var arr1 [3]int = [3]int{1, 2, 3}// 第二种声明 后边有具体值,可以进行推导,前边定义的数组大小和类型可以省略var arr2 = [3]int{1, 2, 3}// 第三种声明 直接赋值可以知道数组大小,可以省略前边的定义数组大小var arr3 = [...]int{1, 2, 3}// 第四种申明 通过索引,进行赋值var arr4 = [...]int{0: 1, 1: 2, 2: 3}fmt.Println(arr1, arr2, arr3, arr4)// [1 2 3] [1 2 3] [1 2 3] [1 2 3]
2. 使用数组
定义了数组,接下来就是使用了,还记得之前要让记住的内容吗(位置很重要,要记住)!
数组就是通过位置去进行修改和调用
var arr [3]intarr[0] = 10arr[1] = 20fmt.Println(arr[0], arr[1])// 10 20
go数组,使用下标的方式进行赋值
就像标兵1号给与冲锋枪一把,标兵2号给与手榴弹三颗
只有叫对了人,才能给与相应的值
3. 数组内存分布
把上边例子进行内存地址打印如下
var arr [3]intarr[0] = 10arr[1] = 20fmt.Println(arr[0], arr[1], arr[2])// 10 20 0fmt.Printf("arr的内存地址=%p, arr[0]的内存地址=%p, arr[1]的内存地址=%p, arr[2]的内存地址=%p", &arr, &arr[0], &arr[1], &arr[2])// arr的内存地址=0xc0000160c0, arr[0]的内存地址=0xc0000160c0, arr[1]的内存地址=0xc0000160c8, arr[2]的内存地址=0xc0000160d0
上边的代码,内存地址如下
- 第三位我没有赋值,也就是数组已经进行了开辟空间,并进行赋值为0。
- 确定了数据类型为int后,进行开辟空间,16位内存地址间隔为8字节(64位操作系统)。
- 从图中可以看到arr的地址和下标为0的地址一样,数组的地址为第一位的地址。
4. 数组的使用
遍历数组,在go中有一种独有的结构,进行遍历数组,即for–range
for index, value := range arr {fmt.Printf("下标index=%d, 遍历值value =%d\n", index, value)}//下标index=1, 遍历值value=20//下标index=2, 遍历值value=0
- 在遍历过程中,index为下标,value为值
- index和value是我自己定义的,当然你可以定义其它名称,但对应的位置含义不变
- 遍历过程中,index,value只在循环体内可见的局部变量
三、切片
上边内容有说,军训时排成一列,让报2-5的上前一步!那这2-5的出来的同学组成了什么呢?呼之欲出的:切片!
1. 啥是切片
炒菜时,切的一片片的菜,这种就叫切片!
切片不是新的东西,而是从之前的东西选择了一部分,被叫做切片!
切片:是引用类型,引用了相应的类型数据,一部分内容
2. 声明切片
切片声明有俩种,一种是直接拿别人的蔬菜进行切片,一种是自己种蔬菜并切片
- 拿别人的蔬菜进行切片
// 创建数组var arr [5]int = [5]int{1, 2, 3, 4, 5}// 创建切片 切出数组中索引为1到3的元素var slice []int = arr[1:3]fmt.Println(slice)// [2 3]
根据创建好的数组,直接根据索引进行切片
- 自己种蔬菜并切片
// 使用make进行创建切片var slice2 []int = make([]int, 5, 10)slice2[0] = 10slice2[1] = 20slice2[2] = 30fmt.Println(slice2)// [10 20 30 0 0]
使用make创建切片结构:var 切片名称 []Type = make([]Type, len, cap)
其中,make参数示意如下:
- []int:数据类型
- len: 切片长度
- cap:切片容量 cap>= len
3. 切片内存
3.1 拿别人的菜进行切片
// 创建数组var arr [5]int = [5]int{1, 2, 3, 4, 5}// 创建切片 切出数组中索引为1到3的元素var slice []int = arr[1:3]fmt.Printf("arr = %p, slice = %p \n", &arr[1], slice)// arr = 0xc00000e458, slice = 0xc00000e458
截取下标1到3的片段(顾头不顾尾法截取),下标为1的地址为:0xc00000e458,而切片就以第一个值的地址为切片地址。
3.2 自己种菜进行切片
// 使用make进行创建切片var slice2 []int = make([]int, 5, 10)slice2[0] = 10slice2[1] = 20slice2[2] = 30fmt.Printf("slice2 = %p \n", slice2)// 0xc0000142d0
使用make,进行创建int类型,长度为5,容量为10的数组,并进行地址引用给切片
数组与切片:数组可以理解为母体,切片可以理解为子体
4. 切片的遍历
切片是数组的一部分,用法和数组基本一样,比如数组的遍历和切片的遍历:
// 使用make进行创建切片var slice2 []int = make([]int, 5, 10)slice2[0] = 10slice2[1] = 20slice2[2] = 30for index, value := range slice2 {fmt.Println(index, value)}//0 10//1 20//2 30//3 0//4 0
附
本篇进行学习数组过程中,由于数组长度是不支持动态扩容,但有这种需求,进而产生了切片的概念!
切片是数组的一部分,而且青出于蓝而胜于蓝,切片是支持动态扩容!
重要说明:
- Go语言中只有数组、字符串和切片本身支持切片操作(使用[start:end]语法)
- 其他数据类型如map、chan、struct等不支持切片操作 切片操作的语法只能用于具有索引访问能力的数据类型
- 字符串切片会产生一个新的字符串,而数组切片会产生一个指向原数组的切片
因此,严格来说,Go语言中只有数组和字符串这两种基本数据类型可以直接进行切片操作,切片本身也可以再次切片。