数组的使用注意事项和细节
1.数组是多个相同类型数据的结合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化。
2.var arr []int 这时arr就是一个slice切片,切片后面专门讲解。
3. 数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
4. 数组创建后,如果没有赋值,有默认值(零值)
数值类型数组:默认值为0
字符串数组:默认值为“”
bool数组:默认值为false
5. 使用数组的步骤 a.声明数组并开辟空间 b.给数组各个元素赋值(默认零值)c.使用数组
6. 数组的下标是从0开始的。
7. 数组下班必须在指定范围内使用,否则报panic:数组越界,比如var arr [5]int 则有效下表为0-4
8. Go的数组属值类型:在默认情况下是值传递,因此会进行拷贝。数组间不会相互影响
9. 如想在其他函数中,去修改原来的数组,可以使用应用传递(指针方式)
10.长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度,看下面案例
要求:生成五个数,并将其反转打印
// 思路 1.生成五个数,rand.Intn()函数 2.当我们得到随机数后,就放到一个数组 int数组 3.反转打印,交换的次数是len/2,倒数第一个和第一个元素交换,倒数第二个和第二个交换 var intArr3 [5]int len := len(intArr3) rand.Seed(time.Now().UnixNano()) for i := 0;i<len;i++ { intArr3[i] = rand.Intn(100) } fmt.Println("交换前=",intArr3) temp := 0 for i:=0;i<len/2;i++{ temp = intArr3[len-1-i] intArr3[len-1-i] = intArr3[i] intArr3[i] = temp } fmt.Println("交换后=",intArr3)
为什么需要切片
先来看一个需求,我们需要一个数组用于保存学生的成绩,但是学生的个数是不确定的,请问怎么办:解决方案使用**切片**
切片的基本介绍
- 切片的英文是slice
- 切片是数组的一个引用,因此切片是引用类型,在精选传递时,遵循引用传参的机制
- 切片的使用和数组类似,遍历切片,访问切片的元素和求切片长度len(slice)都一样。
- 切片的长度是可以变化的,因此切片是一个可以动态变化的数组
- 切片定义的基本语法:
var 切片名 []类型
比如:var a [] int
切片快速入门
package main import ( "fmt" ) func main(){ // 演示切片的基本使用 var intArr [5]int = [...]int{1,22,33,55,99} // 声明/定义一个切片 // slice := intArr[1:3] // l.slice 就是切片名 // 2.intArr[1:3]表示slice引用到intArr这个数组 // 3.引用intArr数组的起始下标为1,最后的下标为3(但是不包含3) slice := intArr[1:3] fmt.Println("intArr=",intArr) fmt.Println("slice 的元素是 =",slice) // 22,33 fmt.Println("slice 的元素个数 =",len(slice)) //2 fmt.Println("slice 的容量 =",cap(slice)) // 切片的容量是可以动态变化 }
切片在内存中形式
基本介绍
为了让大家更加深入的理解切片,我们画图分析一下切片在内存中是如何布局的,这是一个非常重要的知识点
画出前面的内存分布图
对上面的分析总结
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
方式三:定义一个切片,直接就指定具体数组,使用原理类似make的方式
var strSlice []string = []string{"tom","jack","mary"} fmt.Println("strSlice=",strSlice) fmt.Println("strSlice size =",len(strSlice)) fmt.Println("strSlice cap =",cap(strSlice))
方式1和方式2的区别
方式1是直接引用数组,这个数组是事先存在的,程序员是可见的。方式2是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是看不见的。
切片的遍历
切片的遍历和数组一样,也有两种方式
package main import ( "fmt" ) func main(){ // 使用常规的for循环遍历切片 var arr [5]int = [...]int{10,20,30,40,50} slice := arr[1:4] // 20,30,40 for i := 0;i<len(slice);i++{ fmt.Printf("slice[%v]=%v",i,slice[i]) } // 使用for---range方式遍历切片 for i,v := range slice{ fmt.Printf("i=%v v=%v \n",i,v) } }
切片注意事项和细节说明
用append内置函数,可以对切片进行动态追加
切片append操作的底层原理分析
- 切片append操作的本质就是对数组扩容
- go底层会创建一下新的数组newArr(安装扩容后大小)
- 将slice原来包含的元素拷贝到新的数组newArr
- slice重新引入到newArr
- 注意newArr是在底层来维护的,程序员不可见
- 切片是引用类型,所以在传递时,遵循引用传递机制。
切片的拷贝操作
切片使用copy内置函数完成拷贝,举例说明
func main(){ var a []int = []int {1,2,3,4,5} var slice = make([]int,10) fmt.Println(slice) copy(slice,a) fmt.Println(slice) }
string和slice
1.string底层是一个byte数组,因此string也可以进行切片处理
2.string和切片在内存的形式,以“abcd”画出内存示意图
3.string是不可变的,也就是说不能通过str[0]=‘z’方式来修改字符串
4. 如果需要修改字符串,可以先将string->[]byte/或者 []rune->修改->重写转成string.
切片生成斐波那契数列
func fbn(n int) ([]uint64){ // 声明一个切片,切片大小n fbnSlice := make([]uint64,n) // 第一个数和第二个数的斐波那契 为1 fbnSlice[0] = 1 fbnSlice[1] = 1 // 进行for循环来存放斐波那契的数列 for i := 2;i<n;i++{ fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2] } return fbnSlice }
感谢大家观看,我们下次见