此处数组只讲Go语言中和C语言不一样的地方
格式不同:
Go语言定义数组的格式:var ages [3]int
定义的同时初始化
格式 var 数组名称 数组类型 = 数组类型{值}
var ages [3]int = [3]int{1,3,5}
注意点:
1.在定义的同时初始化必须在大括号前写上数据类型,并且数据类型必须相同(数组长度和数据类型)
2.可以和C语言一样部分初始化,但是C语言部分初始化没有被赋值的地方保存了垃圾数据,Go语言中没有被赋值的地方默认为0\
3.数组中数组的名称不能代表数组的地址
第二种方式(省略数组长度)
ages := [...]int{1,3,5}
在这里用...表示数组长度,编译器根据赋值的元素个数自动计算数组的元素个数
先定义后初始化
var ages [3]int
ages = [3]int{1,3,5}
在这里,Go语言可以一次性给数组赋值,当然也可以逐个赋值
数组注意点
Go语言中数组是值类型,数组之间的赋值是值拷贝,而不是地址的拷贝
ages := [3]int{1,3,5}
change(ages)
fmt.println(ages)//1,3,5
func change(num [3]int) {
num[1] = 666
}
2.由于数组是值类型,所以两个数组间可以用" == " " != "进行比较,但是进行值比较的数组必须类型长度完全相同,完全相同!!!
数组的遍历
方法一: 传统的for循环
方法二: for index,value := range ages{
fmt.println(index,value)
}
二维数组
定义二维数组的格式
var 数组名称 [行数][列数]数据类型
var 数组名称 [一维数组的个数][每个一维数组的元素个数]数据类型
注意点:
二维数组也可以省略元素个数, 但是只能省略行数, 不能省略列数
切片(重点)
数组的长度一旦固定,就不能改变,Go语言为了解决这一问题,产生了切片,切片就是可变的数组
切片的底层是结构体
Go语言源码中如此展示
type slice struct{
array unsafe.Pointer // 指向底层数组指针
len int // 切片长度(保存了多少个元素)
cap int // 切片容量(可以保存多少个元素)
}
创建切片的方式
方法一: 通过数组来创造切片
格式:[起始位置:结束位置] 包括起始位置但不包括结束位置,左闭右开的区间
注意点:
切片的长度(len)等于截取到的数组的长度
切片的容量(cap)等于数组的长度减去切片的起始位置
如果不写起始位置,就是从第0个开始截取
如果不写结束位置,就是截取到末尾
var ages[5] = [5]int{1,3,5,7,9}
var sce[] = ages[2:]
fmt.println(sce)//[5,7,9]
fmt.println(len(sce))//3
fmt.println(cap(sce))//3
var sce2[] = ages[:2]
fmt.println(sce2)//[1,3]
fmt.println(len(sce2))//2
fmt.println(cap(sce2))//5
方法二: 通过make函数创建
格式:make([]数据类型,长度,容量)
第一个参数: 创建的数据类型,创建切片前一定要加[]
第二个参数: 告诉系统创建切片的长度
3.第三个参数: 告诉系统创建切片的容量(如果第三个参数不写,那么第三个长度默认和切片的长度相等)
var sce []int = make([]int, 2)
fmt.Println(sce)//[0,0]
fmt.Println(len(sce))//2
fmt.Println(cap(sce))//2
方法三: 通过Go语言提供的语法糖创建
中括号没有值就是切片
特点:用Go语法糖创建的切片的长度和容量相等
var ages []int = []int{1,3,5,7,9}
切片的使用
使用方法一: 切片是可变长度的数组,所以可以像数组一样使用切片,但是索引的长度不能超过切片的len
var sce []int = make([]int,2,5)
sce[2] = 999//报错,索引大于切片长度
fmt.println(sce)
使用方法二: 可以通过预先定义好的函数来使用切片
append函数
append(切片,数据)
append函数作用是在切片len后面添加数据
注意点:
在append函数后面追加的数据超过了切片的cap,那么切片会按照当前容量*2的方式重新扩容,并且sce[0]的值和扩容前的值相等,说明将源切片拷贝了一份作为新切片,切片的地址会发生改变
append函数返回一个切片,由于append扩容的时候需要创建一个新的切片,所以返回一个切片
如果容量不超过1024时,扩容的时候会按照cap*2扩容,如果容量超过1024,扩容的时候会按照原来cap的1/2扩容
如果append追加的数据未超过切片的cap,虽然返回一个切片,但是切片的地址没有改变,如果append追加的数据超过了切片的cap,那么返回的切片的地址会发生改变
var sce []int = make([]int, 2, 3)
fmt.Printf("%p\n",sce)//0xc0420520c0
sce = append(sce, 3)
fmt.Printf("%p",sce)//0xc0420520c0
var sce2 []int = make([]int, 2, 2)
fmt.Printf("%p\n",sce)//0xc0420080c0
sce = append(sce2, 3)
fmt.Printf("%p",sce2)//0xc04200e300
使用方法三:
切片的增删改查
增(append函数)
改(切片名称[索引] = 值)
查
for _,value in range 切片名称{
if value == 3 {
fmt.Println("包含数字3")
}
}
删
我们不仅可以通过数组创建切片,还能通过切片创建切片
通过切片创建的切片,两个切片指向同一个数组
切片的删就是将第二次截取的切片添加到第一次截取的切片后面即可
append(sce[:index],sce[index+1:])
var ages []int = []int{1,3,5,7,9,11}
//假如需要删掉的索引是3(值为7)
var sce []int = ages[:3]
var sce2 []int = ages[4:]
sce3 = append(sce,sce2)
fmt.Println(sce)//[1,3,5,9,11]
数组和切片的辨别
只要定义的时候中括号里有东西,哪怕是...,那定义的就是数组不是切片
切片和函数
可变参数的函数,其中可变参数底层原理就是切片
注意点: 如果函数编写了可变参数, 那么可变参数只能放在形参列表的最后
res:=sum(10, 20, 30)
fmt.Println(res)//60
}
// 注意点: 如果函数编写了可变参数, 那么可变参数只能放在形参列表的最后
func sum(nums ...int) int{
//func sum(value float32, nums ...int) int{
var res int
for _,value := range nums {
res += value
fmt.Println(value)
}
return res
}
func sum(nums []int) int{
// 2.遍历切片
var res int
for _, value := range nums{
res += value
}
return res
}
copy函数
copy(目标切片, 源切片)
注意点:
如果目标切片的容量大于了源切片的容量,那么源切片会一一将数据覆盖到目标切片上
如果目标切片的容量小于了源切片的容量,那么源切片会一一将数据覆盖到目标切片上,多余的数据会被剔除
sce1 := []int{1, 3} // 源切片
sce2 := []int{2, 4, 6} // 目标切片
fmt.Println(sce2)
copy(sce1, sce2)
fmt.Println(sce1)//[2,4]
切片的注意点
1.切片可以再生成新的切片, 两个切片底层指向同一个数组
2.切片和数组不同, 切片不支持== !=操作
3.在Go语言中字符串的底层实现就是切片, 所以可以通过字符串来生成切片
//需求:将字符串"www.it666.com"的首字母改为M
var str string = "www.it666.com"
sce := make([]byte,len(str))
copy(sce,str)
sce[0] = 'M'
fmt.Printf("%s\n", sce)
通过数组创建切片补充
格式: 数组名称[low: high: max]
注意点:
max必须大于第二个参数
如果max不写,那么cap就等于数组长度 - 第一个参数
如果写上max,那么cap就等于max - 第一个参数
var arr [6]int = [6]int{1,3,5,7,9,11}
var sce []int = arr[2:4:4]
fmt.Println(sce) // [5,7]
fmt.Println(len(sce)) // 2
fmt.Println(cap(sce)) // 2
仅仅定义, 没有初识的数组是可以使用的, 但是仅仅定义但是没有初始化的切片是不能使用的
var sce []int
//sce[0] = 666 编译报错
//sce[1] = 888 编译报错
sce = make([]int, 3, 5)
sce[0] = 666
sce[1] = 888
sce[2] = 999
fmt.Println(sce) // [666,888,999]
var sce []int
sce = append(sce, 1)
sce = append(sce, 2)
sce = append(sce, 3)
fmt.Println(sce) // [1,2,3]
切片计算长度不能用unsafe.Sizeof(sce),切片本质是一个结构体,所以Sizeof实际上计算的是结构体的内存,在64位机上为24
var sce []int = []int{1, 3, 5, 7, 9}
//推荐方式:
length := len(sce)
fmt.Println(length)
数组和切片作为函数参数的区别
数组作为函数参数,是值传递,函数内改变不会改变实参的值
切片作为函数参数,是地址传递,函数内改变会改变实参的值