笔上得来终觉浅,绝知此事要躬行
🔥 个人主页:星云爱编程
🔥 所属专栏:Golang
🌷追光的人,终会万丈光芒
🎉欢迎大家点赞👍评论📝收藏⭐文章
![]()
目录
一、数组
1.1 基本介绍
数组可以存放多个同一类型的数据。数组也是一种数据类型,在 Go 中,数组是值类型。
1.2 数组的定义
var 数组名 [数组大小]数据类型
例如:
var arr [3]int
1.3 数组内存布局
数组在内存中的布局如下,以下列代码为例
func main(){
var arr [3]int
//当我们定义完数组后,数组的各个元素有默认值0
fmt.Printf("数组arr的地址%p\n",&arr)
for i:=0;i<len(arr);i++{
fmt.Printf("arr[%d]=%d,地址为%p\n",i,arr[i],&arr[i])
}
}
可以得到的结论:
数组的地址可以通过数组名来获取:&数组名
数组首元素地址等于数组的地址
数组的各个元素的地址间隔是依据数组的类型决定,例如:int64-> 8, int32-> 4
1.4 数组的使用
访问数组元素
数组名 [下标]
例如:arr [0]
初始化数组
//数组的四种初始化 func main(){ //(1)普通初始化 var arr01 [3]int=[3]int{1,2,3} fmt.Println("arr01=",arr01) //(2)省略声明类型 var arr02 = [3]int{4,5,7} fmt.Println("arr02=",arr02) //(3)[...]不指定数组元素个数,但...为固定格式,不能更改 var arr03 = [...]int{4,2,6,1} fmt.Println("arr03=",arr03) //(4)指定位置初始化,按数组下标来指定初始化,没被初始化的默认为0 var arr04 = [...]int{ 3:3,2:2,0:10} fmt.Println("arr04=",arr04) }
3.数组的遍历
方式一:常规遍历
func main(){
var arr=[...]int{1,4,6,7,8}
//遍历数组
for i:=0;i<len(arr);i++{
fmt.Println(arr[i])
}
}
方式二:for-range 结构遍历
基本语法:
for index,value:=range arr{
...
}
说明:
第一个返回值 index 是数组的下标
第二个 value 是 index 下标对应的值
index 和 value 是在 for 循环内部的局部变量
遍历数组元素的时候,若不想用下标 index,可以直接把下标 index 标位下划线_
index 和 value 的名称是用户自定义的,一般命名为 index 和 value
案例:
func main(){
var arr=[...]int{1,4,6,7,8}
//遍历数组
for index,value:=range arr{
fmt.Printf("arr[%d]=%d\n",index,value)
}
var arr2=[...]string{"hello","baby","xinxin"}
//遍历数组
for i ,v:=range arr2{
fmt.Printf("arr2[%d]=%v\n",i,v)
}
}
1.5 数组使用细节
数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化
var arr [] int 这时 arr 就是一个 slice 切片,切片下一节就讲
数组中的元素可以是任何数据,包括值类型和引用类型,但是不能混用
数组的下标是从 0 开始的, 下标必须在指定范围内使用,否则 panic:数组越界
数组创建后,若没有赋值,有默认值,数值类型默认为 0,字符串类型默认为“”,bool 类型默认为 false
Go 的数组属于值类型,在默认情况下是值传递,因此会进行值拷贝,数组间不会相互影响
长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度
二、切片
2.1 基本介绍
切片的英文是 slice
切片是数组的一个引用类型,在进行值传递时,遵守引用传递的机制
切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度都一样
切片的长度是可以变化的,因为切片是一个可以动态变化的数组
切片定义的基本语法:
var name []int
例子
func main(){
var arr=[...]int{1,3,5,7,9}
//声明定义一个切片
slice:=arr[1:3]
//slice:=arr[1:3]
//解读:
//(1)slice就是切片名
// (2)arr[1:3]表示slice引用到arr这个数组
// (3)引用arr数组的起始下标为1,最后的下标为3,但是不包含3,即[1:3)
fmt.Println("arr=",arr)
fmt.Println("slice的元素=",slice)
fmt.Println("slice的元素个数=",len(slice))
fmt.Println("slice的容量=",cap(slice))//切片的容量是可以动态变化的,大小一般为元素个数的两倍
}
2.2 切片内存机制
slice 是一个引用类型
slice 从底层来说,其实就是一个数据结构(struct 结构体)
type slice struct{ ptr*[2]int len int cap int }
总结:
代码示例:
func main(){
var arr=[...]int{1,3,5,7,9}
//声明定义一个切片
slice:=arr[1:3]
//slice:=arr[1:3]
//解读:
//(1)slice就是切片名
// (2)arr[1:3]表示slice引用到arr这个数组
// (3)引用arr数组的起始下标为1,最后的下标为3,但是不包含3,即[1:3)
fmt.Println("arr=",arr)
fmt.Println("slice的元素=",slice)
fmt.Println("slice的元素个数=",len(slice))
fmt.Println("slice的容量=",cap(slice))//切片的容量是可以动态变化的,大小一般为元素个数的两倍
fmt.Println("arr[1]的地址",&arr[1])
fmt.Println("slice的地址",&slice[0])
}
2.3 切片的使用
方式一:定义一个切片,然后让切片去引用一个已经创建好的数组
方式二:通过 make 来创建切片
基本语法:
var 切片名 []type=make([]type,len,[cap])说明:type 为数据类型
len 为切片大小
cap 指定切片容量
案例:
//演示make创建切片
func main(){
var slice []int=make([]int,10,20)
slice[0]=1
slice[2]=3
slice[4]=5
slice[6]=7
slice[8]=9
for index,value:=range slice{
fmt.Printf("slice[%d]=%d\n",index,value)
}
}
小结:
通过 make 方式创建切片可以指定切片的大小和容量
如果没有给切片的各个元素赋值,那么就会使用默认值(机制同数组)
通过 make 方式创建的切片对应的数组是由 make 底层维护,对外不可见,即只能通过 slice 去访问各个元素
方式三:定义一个切片,直接就指定具体数组,使用原理类似 make 的方式
案例演示:
func main(){
var slice []string=[]string{"fake","make","right","evote"}
fmt.Println("slice=",slice)
fmt.Println("slice size=",len(slice))
fmt.Println("slice cap=",cap(slice))
}
面试题:方式一和方式二的区别
方式一是直接引用数组,这个数组是事先存在的,程序员可见的
方式二是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是看不见的
2.4切片的遍历
切片的遍历与数组一样,有2种方式
(1)for循环常规遍历
func main(){
var slice []int=make([]int,5,10)
slice[0]=1
slice[2]=3
slice[4]=5
for i:=0;i<len(slice);i++{
fmt.Printf("slice[%d]=%d\n",i,slice[i])
}
}
(2)for-range 结构遍历
func main(){
var slice []int=make([]int,5,10)
slice[0]=1
slice[2]=3
slice[4]=5
for index,value:=range slice{
fmt.Printf("slice[%d]=%d\n",index,value)
}
}
2.5切片的使用细节
切片初始化时 var slice=arr[startIndex:endIndex]
说明:从arr数组下标为startIndex,取到下标为endIndex的元素(不含arr[ednIndex]),即取arr数组中[startIndex,endIndex)的元素
切片初始化时,仍然不能越界,范围在[0~len(arr)]之间,但是可以动态增长
一些引用的简写:
(1)var slice =arr[0:end]可以简写为var slice=arr[:end]
(2)var slice =arr[start:len(arr)]可以简写为 var slice= arr [start:]
(3)var slice =arr[0,len(arr)]可以简写为 var slice =arr[:]
cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素
切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组或make一个空间供切片来使用
切片可以继续切片
用append内置函数,可以对切片进行动态追加
(1)切片append操作的本质就是对数组扩容
(2)Go底层会创建新的数组newarr(安装扩容后大小)
(3)将slice原来包含的元素拷贝到新的数组newarr
(4)slice重新引用newarr
(5)newarr是在底层维护的,程序员不可见
案例:
func main(){ var slice []int=[]int{23,44,55,77,88} //用append内置函数,对切片进行动态追加 slice=append(slice,100,200) fmt.Println("slice=",slice) //通过append将切片slice追加给slice,相当于复制一遍 slice=append(slice,slice...) fmt.Println("slice=",slice) }
切片的拷贝操作
切片使用copy内置函数完成拷贝,例如:
//拷贝操作
func main(){
var slice []int=[]int{23,44,55,77,88}
var slice2 =make([]int,10)
copy(slice2,slice)//将slice拷贝给slice2
fmt.Println("slice=",slice)
fmt.Println("slice2=",slice2)
}
注意事项:
copy(para1,para2)参数的数据类型是切片
上例中,slice和slice2的数据空间是独立的,互不影响
思考题:下面的代码有没有错误?
func main(){
var a[]int =[]int{1,2,3,4,5}
var slice =make([]int,1)
fmt.Println(slice)//1
copy(slice,a)
fmt.Println(slice)//1
}
不会报错
2.6string和slice
string底层是一个byte数组,因此string也可以进行切片处理
string是不可变的,也就是说不能通过str[0]='a'方式来修改字符串
若要修改字符串,可以先将string转为[]byte或[]rune,修改后,重写转成string
案例
func main(){
str:="helloworld"
//string底层是一个byte数组,因此string也可以进行切片处理str := "helloworld"
//使用切片得到world
slice:=str[5:]
fmt.Println("slice=",slice)
//string是不可变的,也就说不能通过 str[0]='z’方式来修改字符串
//str[0]='z'[编译不会通过,报错,原因是string是不可变]
//如果需要修改字符串,可以先将string->[]byte /或者[]rune ->修改->重写转成string
//将helloworld改为aelloworld
arr:=[]byte(str)
arr[0]='a'
str=string(arr)
fmt.Println("str=",str)
//若是我们想把helloworld改为 哇elloworld,则不能用[]byte
//我们转成[]byte后,可以处理英文和数字,但是不能处理中文
//原因是[]byte 字节来处理 ,而一个汉字,是3个字节,因此就会出现乱码
// 解决方法是 将 string 转成[]rune 即可, 因为[]rune是按字符处理,兼容汉字
arr2:=[]rune(str)
arr2[0]='哇'
str=string(arr2)
fmt.Println("str=",str)
}
结语
感谢您的耐心阅读,希望这篇博客能够为您带来新的视角和启发。如果您觉得内容有价值,不妨动动手指,给个赞👍,让更多的朋友看到。同时,点击关注🔔,不错过我们的每一次精彩分享。若想随时回顾这些知识点,别忘了收藏⭐,让知识触手可及。您的支持是我们前进的动力,期待与您在下一次分享中相遇!
路漫漫其修远兮,吾将上下而求索。