#### **数组介绍**
数组可以存放多个同一类型数据。数组也是一种数据类型,在Go中,数组是值类型。
看一个问题
一个养鸡场有6只鸡,它们的体重分别是3kg,5kg,1kg,3.4kg,2kg,50kg。请问这六只鸡的总体重是多少?平均体重是多少?请你编一个程序。=》数组
![](https://img.kancloud.cn/61/a8/61a8f3e18be12e0936353091330a4372_722x473.png)
对上面代码的总结
1)使用数组来解决问题,程序的可维护性增加.
2)而且方法代码更加清晰,也容易扩展。
**数组定义和内存布局**
var数组名\[数组大小\]数据类型
vara\[5\]int
初值a\[0\]=1a\[1\]=30....
数组在内存布局(重要)
![](https://img.kancloud.cn/1a/11/1a11c6485d624a78a6e7521e0eb618c0_769x257.png)
对上图的总结
1)数组的地址可以通过数组名来获取&intArr
2)数组的第一个元素的地址,就是数组的首地址
3)数组的各个元素的地址间隔是依据数组的类型决定,比如int64->8int32->4..
![](https://img.kancloud.cn/0e/9f/0e9fa0df069f6faaf02b4bf77e3715be_743x292.png)
**数组的使用**
数组名\[下标\]比如:你要使用a数组的第三个元素a\[2\]
**四种初始化数组的方式**
![](https://img.kancloud.cn/c0/90/c09028a66c32c4bbed4043641c4cc085_663x324.png)
**数组的遍历**
1:常规遍历:for循环
2:for-range结构遍历这是Go语言一种独有的结构,可以用来遍历访问数组的元素。
![](https://img.kancloud.cn/54/41/5441286bb5c7618dadd14ae9efcab27d_925x290.png)
![](https://img.kancloud.cn/c7/6c/c76c2b8e854e9b74fea38829d622ead0_716x417.png)
**数组使用的注意事项和细节**
1)数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化
![](https://img.kancloud.cn/89/d3/89d337df5b3b53da14015fe975adf082_701x365.png)
2) var arr \[\]int 这时 arr 就是一个 slice 切片,切片后面专门讲解,不急哈.
3) 数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
4) 数组创建后,如果没有赋值,有默认值(零值)
数值类型数组:默认值为 0
字符串数组: 默认值为 ""
bool 数组: 默认值为 false
![](https://img.kancloud.cn/81/3d/813de0d78dd18cf4b6e7578b2747d848_654x220.png)
5) 使用数组的步骤 1\. 声明数组并开辟空间 2 给数组各个元素赋值(默认零值) 3 使用数组
6) 数组的下标是从 0 开始的
![](https://img.kancloud.cn/6e/d3/6ed3d3356e549ceb9ef4157c37c66747_612x124.png)
7) 数组下标必须在指定范围内使用,否则报 panic:数组越界,比如 var arr \[5\]int 则有效下标为 0-4
8) Go 的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影响
![](https://img.kancloud.cn/10/2e/102e6d80aa08244f28037c42cedf419c_814x244.png)
9) 如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)
![](https://img.kancloud.cn/ba/6b/ba6b1b69b097cde416c383d9bf1260a3_839x244.png)
10) 长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度,看下面案例
![](https://img.kancloud.cn/f4/96/f496adfafe051f463dfcacd1199ceb29_805x305.png)
#### **切片**
1) 切片的英文是 slice
2) 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。
3) 切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度 len(slice)都一样。
4) 切片的长度是可以变化的,因此切片是一个可以动态变化数组。
5) 切片定义的基本语法: var 切片名 []类型 比如:var a [] int
**快速入门**
![](https://img.kancloud.cn/7f/7c/7f7cfdaf05cf573d37eca9822f38bd72_736x548.png)
**切片在内存中形式(重要)**
![](https://img.kancloud.cn/43/37/43379ba923632d8da09c3605c779f464_923x179.png)
对上面的分析图总结
1. slice 的确是一个引用类型
2. slice 从底层来说,其实就是一个数据结构(struct 结构体)
```
type slice struct {
ptr *[2]int
len int
cap int
}
```
**切片的使用**
第一种方式:定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例就是这样的。
第二种方式:通过 make 来创建切片.
基本语法:var 切片名 []type = make([]type, len, [cap])
参数说明: type: 就是数据类型 len : 大小 cap :指定切片容量,可选, 如果你分配了 cap,则要求 cap>=len.
![](https://img.kancloud.cn/6c/3a/6c3a2a228a0ab77aed5d909465caa7bf_924x228.png)
对上面代码的小结:
1) 通过 make 方式创建切片可以指定切片的大小和容量
2) 如果没有给切片的各个元素赋值,那么就会使用默认值\[int , float=> 0 string =>”” bool => false\]
3) 通过 make 方式创建的切片对应的数组是由 make 底层维护,**对外不可见,即只能通过 slice 去 访问各个元素.**
第 3 种方式:定义一个切片,直接就指定具体数组,使用原理类似 make 的方式
![](https://img.kancloud.cn/c7/43/c743455989afbc7e17b981f4da0d62fa_754x210.png)
方式 1 和方式 2 的区别(面试)
![](https://img.kancloud.cn/63/73/637391d677207364888089ecd5add416_625x201.png)
**切片的遍历**
切片的遍历和数组一样,也有两种方式
for 循环常规方式遍历
for-range 结构遍历切片
![](https://img.kancloud.cn/c2/08/c20849a3f30466570d61416676bd683c_694x343.png)
**切片的使用的注意事项和细节讨论**
1) 切片初始化时 var slice = arr\[startIndex:endIndex\] 说明:从 arr 数组下标为 startIndex,取到 下标为 endIndex 的元素(不含 arr\[endIndex\])。
2) 切片初始化时,仍然不能越界。范围在 \[0-len(arr)\] 之间,但是可以动态增长.
var slice = arr\[0:end\] 可以简写 var slice = arr\[:end\] var slice = arr\[start:len(arr)\] 可以简写: var slice = arr\[start:\] var slice = arr\[0:len(arr)\] 可以简写: var slice = arr\[:\]
3) cap 是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
4) 切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者 make 一 个空间供切片来使用
5) 切片可以继续切片\[案例演示\]
![](https://img.kancloud.cn/cd/e6/cde62b84c44012cb9215015ca8737f85_687x395.png)
6) 用 append 内置函数,可以对切片进行动态追加(注意直接下标得方式长度有限制长度为切片初始化的默认长度超过长度限制)
![](https://img.kancloud.cn/68/07/68074cd79beceba535909ff05ce9bd1a_735x207.png)
![](https://img.kancloud.cn/dc/8e/dc8eaeef4ae9f17fe5b9a29aeba75be1_732x256.png)
切片 append 操作的底层原理分析:
切片 append 操作的本质就是对数组扩容
go 底层会创建一下新的数组 newArr(安装扩容后大小)
将 slice 原来包含的元素拷贝到新的数组 newArr
slice 重新引用到 newArr
注意 newArr 是在底层来维护的
![](https://img.kancloud.cn/76/78/76784ae8f64eba791e51fe6a0dfc0173_623x217.png)
7) 切片的拷贝操作
切片使用 copy 内置函数完成拷贝,举例说明,程序员不可见
![](https://img.kancloud.cn/c0/38/c0384630176768c20d1ab6301b1d0d85_667x201.png)
对上面代码的说明:
(1) copy(para1, para2) 参数的数据类型是切片
(2) 按照上面的代码来看, slice4 和 slice5 的数据空间是独立,相互不影响,也就是说 slice4\[0\]= 999,
slice5\[0\] 仍然是 1
8) 关于拷贝的注意事项
![](https://img.kancloud.cn/d6/63/d66317cae9808a57ea4cb5f341109e97_870x288.png)
9) 切片是引用类型,所以在传递时,遵守引用传递机制。看两段代码,并分析底层原理
![](https://img.kancloud.cn/20/91/209162436859828eb7f682e238b134d0_801x300.png)
**string 和 slice**
1) string 底层是一个 byte 数组,因此 string 也可以进行切片处理 案例演示:
![](https://img.kancloud.cn/65/5e/655ee1aa7140a2d5b627dea95951124a_598x211.png)
2) string 和切片在内存的形式,以 "abcd" 画出内存示意图
![](https://img.kancloud.cn/d8/db/d8db45d330733207f86f49b2f2c84680_669x278.png)
3) string 是不可变的,也就说不能通过 str\[0\] = 'z' 方式来修改字符串
![](https://img.kancloud.cn/e5/99/e59980864e08cff2a3b38082228a547f_677x74.png)
4) 如果需要修改字符串,可以先将 string -> []byte / 或者 []rune -> 修改 -> 重写转成 string
![](https://img.kancloud.cn/bd/47/bd47a8498b40d476465e719084002901_761x327.png)
编写一个函数 fbn(n int) ,要求完成
1) 可以接收一个 n int
2) 能够将斐波那契的数列放到切片中
3) 提示, 斐波那契的数列形式:
arr[0] = 1; arr[1] = 1; arr[2]=2; arr[3] = 3; arr[4]=5; arr[5]=8
![](https://img.kancloud.cn/17/a9/17a9de2cf3d18e777ea1f0f0a176b7a2_750x782.png)
**二维数组**
![](https://img.kancloud.cn/c4/0b/c40b487febbe7bfe3180585991b45809_722x637.png)
**使用方式 1: 先声明/定义,再赋值**
语法: var 数组名 [大小] [大小\]类型
比如: var arr [2][3]int , 再赋值。
使用演示
二维数组在内存的存在形式(重点)
解释:指针是16进制显示,两个指针相差24个字节因为 int占8个3*8地址为第二个指针
![](https://img.kancloud.cn/07/26/07265f826b48da06dfa199282946c953_831x265.png)
![](https://img.kancloud.cn/d4/f0/d4f0f5da4a11f406894f5a0a31302940_804x252.png)
**使用方式 2: 直接初始化**
声明:var 数组名 \[大小\]\[大小\]类型 \= \[大小\]\[大小\]类型{{初值..},{初值..}}
赋值(有默认值,比如 int 类型的就是 0)
![](https://img.kancloud.cn/c3/f1/c3f152868cd3ca1dae0c65c2717acb36_527x91.png)
**二维数组在声明/定义时也对应有四种写法\[和一维数组类似\]**
var 数组名 \[大小\]\[大小\]类型 \= \[大小\]\[大小\]类型{{初值..},{初值..}}
var 数组名 \[大小\]\[大小\]类型 \= \[...\]\[大小\]类型{{初值..},{初值..}}
var 数组名 \= \[大小\]\[大小\]类型{{初值..},{初值..}}
var 数组名 \= \[...\]\[大小\]类型{{初值..},{初值..}}
**二维数组的遍历**
双层 for 循环完成遍历
for-range 方式完成遍历
![](https://img.kancloud.cn/58/78/5878133070c5fc2323d9ec07d0346924_617x372.png)