位置 2 处的索引超出数组边界(不能超出 105)_第九章: 数组和切片

这篇博客详细介绍了Go语言中的数组和切片。数组是相同数据类型的元素集合,其长度固定,赋值时若长度不匹配会导致错误。切片则是数组的视图,不存储数据,提供了一种灵活的方式来操作数组。博客内容包括数组的定义、值类型、长度获取、遍历以及多维数组的概念。接着,讲解了切片的创建、修改、长度与容量、通过`make`创建以及`append`方法的使用,并探讨了切片作为函数参数的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

272755c67a0c57b6690a5df16819c1c9.png

数组和切片

1. 数组

1.1 定义

什么是数组?数组是一堆相同数据类型的集合,需要强调的是这个集合里的数据类型一定是一致的 .

如何定义一个数组: 数组的格式[n]T ,n是这个集合中元素的个数,T是类型type

下面是一个简单的案例:

package main

import "fmt"

func main(){
    var arr [10]int
    fmt.Println(arr)
}
//[0 0 0 0 0 0 0 0 0 0]

数组的中索引是从0开始的,下面我们来看一下如何给数组赋值

package main

import "fmt"

func main(){
    var arr [10]int
    fmt.Println(arr)
    arr[0] = 0
    arr[1] = 1
    arr[2] =2
    fmt.Println(arr)
    fmt.Println(arr[1])
}
/**
[0 0 0 0 0 0 0 0 0 0]
[0 1 2 0 0 0 0 0 0 0]
1
*/

通过上面的案例我们可以看到以下几点:

  • 声明一个数组类型的变量,和其他类型一样,会有零值
  • 数组的赋值,可以通过下表索引的方式进行赋值
  • 数组可以通过变量名和说的方式获取数组里面的值

上面我们发现如何10个元素,赋值的话需要写十个arr[index],是否有其他简单的方式:下面是逐渐演进的过程:

func main(){
    var arr [3]int = [3]int{1,2,3}//最完整的声明赋值
    fmt.Println(arr)
    var arr2 = [3]int{3,4,5}//省略了变量类型,让编译器进行推断
    fmt.Println(arr2)
    arr3:= [3]int{4,5,6}//省略了var 和变量类型,同样让编译器进行推断,当然这个只允许定义局部变量
    fmt.Println(arr3)
    arr4 := [...]int{8,9,0}// 三个点表示 让编译器去数他的长度
    fmt.Println(arr4)
}

如果遇到长度和自己的初始化值不一致时会怎样:

arr5:=[4]int{1,2}
fmt.Println(arr5)
//[1 2 0 0]
arr6 := [2]int{1,2,3,4}
fmt.Println(arr6)
//array index 2 out of bounds [0:2]

我们发现如果传入的值和长度个数不匹配时,如果传入的值个数小于数组的长度,则会自动填充零,否则会报超出边界的错误.

1.2 值类型

数组是值类型的,这意味值,当将一个数组类型的变量赋值给另一个变量时执行的是复制操作,而不是引用,下面我们验证一下

package main

import "fmt"

func main(){
    var array =[...]int{1,2,3}
    array2 := array
    fmt.Printf("array内存地址是%pn",&array)
    fmt.Printf("array2内存地址是%pn",&array2)
}
/**
array内存地址是0xc000012340
array2内存地址是0xc000012360
*/

上面例子中我们发现,两个变量的指针指向不是一个地方,说明他们是存储了两份,也就是所谓的值类型

1.3 获取数组的长度

可以通过len获取数组的长度,也就是这里面有多少个元素,

package main

import "fmt"

func main(){
    arrayLen:=[10]int{1,2,3}
    fmt.Println(len(arrayLen))
}
//10

通过上面的例子我们可以看出len获取的是其实[n]T中的n,并不是赋值的个数

1.3 遍历数组

  • 通过for循环进行遍历:

go for i:=0;i<len(arrayLen);i++{ fmt.Println(arrayLen[i]) }

  • 通过range进行遍历:

go for index,value:=range arrayLen{ fmt.Println(index,value) } }

1.4 多维数组

有些时候,一维数组并不能很好的满足我们的需求,比如做一个俄罗斯方块游戏,随机生成的积木,其实就是一个二维数组,或者你也可以称之为矩阵,下面我们看一下如何定义二维数组:

a := [2][2]string{
    {"张三","李四"},
    {"王五","赵六"},
}
fmt.Println(a)
//[[张三 李四] [王五 赵六]]

遍历多为数组的话和一维的类似,只不过需要多嵌套一层

2. 切片

什么是切片?

切片可以看作是数组的一个装饰器,或者有的人他是数组的视图,这些都是基于他的特性来的,他本身不包含任何数据 ,他是已有数据的 引用,并对引用的数据增加了一些属性和处理方法.

2.1 创建切片

切片格式为[]T,与数组不同之处是,括号里面没有长度,下面是一个实例

package main

import "fmt"

func main(){
    arr :=[3]int{1,2,3}
    slice := arr[:2]
    fmt.Println(slice)
}

上面就是基于数组创建的切片的示例,语法结构为arr[start:end] 索引从0开始,如果没有的start表示从0开始,同理没有end表示start到结尾,上面的arr[:2]表示索引从0-1,两个个元素,需要注意的是这个区间是左闭右开区间

当然也可以通过直接赋值的方式进行创键,这种方式其实也是go在内部创建了一个数组,实例如下:

sliceValue:=[]int{1,2,3}

2.2 修改切片中的值

因为切片并不保存任何数据,所以当修改切片中某一个元素的值时,实际改的是他所引用的数组中元素的值,下面是一个例子,我们来验证一下:

package main

import "fmt"

func main(){
    arr:=[...]int{1,2,3,4,5}
    slice :=arr[:2]
    fmt.Println("原始的arr值:",arr)
    fmt.Println(slice)
    slice[1] = 100
    fmt.Println("修改slice值以后,arr的值:",arr)
}
/**打印结果如下:
原始的arr值: [1 2 3 4 5]
[1 2]
修改slice值以后,arr的值: [1 100 3 4 5]
*/

2.3 切片的长度和容量

切片的长度:len表示这个切片中的元素个数(end-start即可)

切片的容量cap: 表示基于底层的数组,位置从start开始到末尾的个数

下面我们通过一个例子说明:

package main

import "fmt"

func main(){
    var arr1 =[...]int{1,2,3,4,5}
    var slice1 =arr1[1:3]
    fmt.Println("arr1是:",arr1)
    fmt.Println("slice1是:",slice1)
    fmt.Println("slice1的length",len(slice1))
    /**
    解释说明:
        slice1中的元素是[2,3],第一个元素2在arr1中的索引是1,那么cap就是在arr1中数,从1到末尾的长度,一共是3个元素,也就是数组的长度-slice的start=cap
     */
    fmt.Println("slice1的capcity",cap(slice1))
}

2.4 通过make创建切片

在没有数组时,如何创建切片?这时可以使用make进行创建,语法为:

make([]Type,len,cap)

type是要依托于什么类型的数组,len是切片元素数量,cap是容量

下面是一个案例

package main

import "fmt"

func main(){
    sliceMake := make([]int,2)
    for index,value:=range sliceMake{
        fmt.Println(index,value)
    }
}

2.5 切片追加元素

我们知道数组的长度在定义的时候就已经固定好了,是无法更改了,因为一旦更改了,那么他的数据类型也就变化了,因为长度是数组的数据类型的一部分,就像int8 与int32是两种类型.但是切片没有这个限制,他可以使用append来进行扩容,下面是append方法的使用:

package main

import "fmt"

func main(){
    sliceAppend:=[]string{"张三","李四","王五"}
    fmt.Println(sliceAppend)
    sliceAppendAfter :=append(sliceAppend,"赵六")
    fmt.Println(sliceAppendAfter)
}

可能你会有这样的疑问,slice不包含任何数据,全都依赖数组,但是数组的长度又是不可变的,这是不是冲突了,其实,我们在使用append的时候,如果超出原来数组的容量后go会产生一个新的数组,下面我们来验证一下

package main

import "fmt"

func main(){
    //首先创建一个数组
    arr :=[...]string{"a","b","c"}
    fmt.Printf("第一个元素的位置:%pn",&arr[0]) //第一个元素的位置:0xc000070330
    //根据上面的数组创建slice
    sliceArr:= arr[:2]//a,b
    fmt.Printf("slice中的a元素的指针:%pn",&sliceArr[0]) //slice中的a元素的指针:0xc000070330
    //通过上面的例子我们发现他们地址是相同的,接下来我们对sliceArr进行扩容处理

    sliceArrAppend := append(sliceArr,"d")
    fmt.Println(sliceArrAppend)
    fmt.Printf("sliceArrAppend中a元素的位置:%pn",&sliceArrAppend[0])
    //我们继续扩容
    sliceArrAppend := append(sliceArrAppend,"d","esdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdf","sdfsdfsdfsdfsd","sdfsdfsdfsdfsd","sdfsdfsdfsdfsdf")
    fmt.Printf("sliceArrAppend2中a元素的位置%pn",&sliceArrAppend2[0])

}
/**结果为:

第一个元素的位置:0xc0000b6330
slice中的a元素的指针:0xc0000b6330
[a b d]
sliceArrAppend中a元素的位置:0xc0000b6330
sliceArrAppend2中a元素的位置0xc0000d2000
*/

通过上面的例子正好验证了上面说的sliceArrAppend是没有超出原有数组的容量是,后面继续新增元素,结果超出原有的数组容量,所以go帮我们创建了一个新的底层数组,证据就是第一个a元素的指针发生了变化

上面都是一些使用元素追加slice的按理,其实还可以使用切片追加切片,通过使用...三个点来操作,下面是示例

package main

import "fmt"

func main(){
    sliceA := []string{"a","b","c"}
    sliceB := []string{"d","e","f"}
    sliceA = append(sliceA,sliceB...)
    fmt.Println(sliceA)
}
//[a b c d e f]

2.6 函数传参

当slice作为函数参数使用的时候,其实更能体现切片是数组的引用这一观点,下面是一个实例

package main

import "fmt"

func passSlice(parmeter []int){
    for i:=range parmeter{
        parmeter[i]-=1
    }

}

func main(){
    slice :=[]int{1,3,4}
    fmt.Println(slice)
    passSlice(slice)
    fmt.Println(slice)
}
//[1 3 4]
//[0 2 3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值