Golang-数组与切片

数组

  数组可以存放多个同一类型数据。数组也是一种数据类型,在Go中,数组是值类型
  例子

package main

import "fmt"

func main() {

   //定义一个数组
   var arr [5]int

   //给数组的每个元素赋值
   arr[0] = 3
   arr[1] = 6
   arr[2] = 9
   arr[3] = 12
   arr[4] = 15

   for i := 0; i < len(arr); i++ {
      fmt.Printf("索引[%d]的值为:%d\n",i,arr[i])
   }
}

数组定义

var 数组名 [数组大小]数据类型

例如

var a [10]int

进行赋初始值

a[0] = 23
a[1] = 30
....

其下标也是从0开始。

数组的地址可以通过数组名来获取 &arr
数组的第一个元素的地址就是数组的首地址
数组的各个元素的地址间隔是依据数组的类型决定的,比如 int64 ->8 int32 ->4

func main(){
    var arr [3]int
    fmt.Println(arr)
    arr[0] = 1
    arr[1] = 2
    arr[2] = 3
    fmt.Println(arr)
    fmt.Printf("arr的地址=%p arr[0]的地址=%p arr[1]的地址=%p arr[1]的地址=%p",&arr,&arr[0],&arr[1],&arr[2])
}

数组初始化
  四种初始化数组的方法

//方法一
var arr1 [3]int = [3]int{1,2,3}
fmt.Println("arr1=",arr1)

//方法二
var arr2 = [3]int{1,2,3}
fmt.Println("arr2=",arr2)

//方法三
//[...]是规定的写法
var arr3 = [...]int{1,2,3}
fmt.Println("arr3=",arr3)

//方法四
var arr4 = [...]int{1:300, 0:500, 2:90}
fmt.Println("arr4=",arr4)

数组的遍历
方法一:常规遍历

var arr [3]int
for i:=0; i< len(arr); i++{
    fmt.Printf("arr[%d]=%v\n",i, arr[i])
}

方法二:for-range结构遍历

func main(){
    str := [...]string{"小名","小白","小黑"}
    for i,v := range str{
        fmt.Printf("i=%v  v=%v\n",i,v)
        fmt.Printf("str[%d]=%v\n",i,str[i])    
    }
    
    for _, v := range str{
        fmt.Printf("元素的值=%v\n",v)    
    }
}

对上述for-range结构进行说明:

  1. 第一个返回的值是数组的下标,也就是上述的i
  2. 第二个是在该下标位置的值,也就是上述的v
  3. i和v仅在for循环内部可见的局部变量
  4. 对数组进行遍历的时候,如果不想使用下标i,可以直接把下标i换成_
  5. i和v的名称不是固定的,可自己定义

扩展
从终端循环输入5个数字,保存到float64数组中,并输出

var num [5]float64

for i:=0; i< len(num); i++{
    fmt.Printf("请输入第%d个元素的值\n",i+1)
    fmt.Scanln(&num[i])
}

for i:=0; i < len(num); i++{
    fmt.Printf("num[%d]=%v\n",i, num[i])
}

注意事项

  1. 数组是多个相同类型数据的组合。一旦定义了,长度是固定不变的。
  2. 数组中的元素可以是任何类型的数据,包括值类型和引用类型。
  3. 数组创建后,如果没有赋值,其值就为默认值
    数值类型数组:默认值为0
    字符串数组:默认值为""
    bool数组:默认值为false
  4. 数组的下标是从0开始的。
  5. 数组下标必须在指定范围内使用,否则会报错。
  6. 如果想在其他函数中修改原来的数组,可以使用引用传递(指针方式)
arr := [3]int{1,2,3}
test(&arr)
fmt.Println("arr=",arr)



func test(arr *[3]int){
    (*arr)[0] = 32
}

切片

  切片(slice)是数组的一个引用,所以切片是引用类型;切片的长度是可以变化的,因此切片是一个可以动态变化数组

定义

var 切片名 []类型

如: var sli []int

func mian(){
    var arr [5]int = [...]int{1,2,3,4,5}
     //定义一个切片
    sli := arr[1:3]
    //上面表示切片sli引用到arr这个数组,引用arr从下标1开始到下标3,但是不包含3
    fmt.Println("arr=",arr)
    fmt.Println("sli的元素是=",sli) // 2,3
    fmt.Println("sli的元素个数=",len(sli)) //2
    fmt.Println("sli的容量=",cap(sli)) //切片的容量是可以动态变化的
}

切片使用
方式一:定义一个切片,然后让切片去引用一个已经创建好的数组,如上述的

var arr [5]int = [...]int{1,2,3,4,5}
//定义一个切片
sli := arr[1:3]

方式二:通过make来创建切片

其语法:var 切片名 []type = make([]type,len,[cap])
参数说明:type就是数据类型,len是大小,cap是指定切片容量,为可选参数,使用要确保cap>=len

例子:
var sli []str = make([]string, 5, 10)
sli[1] = "xia"
sli[3] = "ming"

fmt.Println(sli)
fmt.Println("sli的size=",len(sli))
fmt.Println("sli的cap=",cap(sli))

通过make方式创建切片可以指定切片的大小和容量;
没有给切片的各个元素赋值,就会使用默认值,默认值和数组的一致;
通过make方式创建的切片对应的数组是由make底层维护,对外不可见,只能通过slice去访问各个元素。

方式三:定义一个切片,直接指定具体数组

var sli []string = []string{"aa","bb","cc"}
fmt.Println("sli=",sli)
fmt.Println("sli的size=",len(sli))
fmt.Println("sli的cap=",cap(sli))

注意:方式一和方式二有什么区别?
  方式一是直接通过引用数组,这个数组是事先存在的,程序员可见的。
  方式二是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是不可见的。

切片遍历

切片的遍历也是有两种方式:

  1. for循环常规方式
  2. for-range结构遍历
func main(){
    //常规for
    var arr [5]int = [...]int{1,2,3,4,5}
    sli := arr[1:4]
    for i := 0; i< len(sli); i++{
        fmt.Printf("sli[%v]=%v",i,sli[i])    
    }
    fmt.Println()
    //使用for-range方式
    for i, v :=range sli{
        fmt.Printf("i=%v v=%v",i,v)    
    }
} 

切片追加
  使用其内置函数append,可以对切片进行动态追加

var sli []int = []int{1,2,3}
sli = append(sli,4,5,6)
fmt.Println("sli=",sli)

//也可通过append将切片sli追加给sli
sli = append(sli,sli...) //1,2,3,4,5,6 1,2,3,4,5,6
fmt.Println("sli=",sli)

切片的拷贝
  切片使用copy内置函数完成拷贝

var sli1 []int = []int{1,2,3,4,5}
var sli2 = make([]int,10)
copy(sli2,sli1)
fmt.Println("sli1=",sli1) //1,2,3,4,5
fmt.Println("sli2=",sli2) //1,2,3,4,5,0,0,0,0,0

  sli1和sli2的数据空间是独立,相互不影响,拷贝执行后sli1的值不变

切片对string操作
  string底层是一个byte数组,因此string也可以进行切片:

func main(){
    str := "helloworld"
    sli := str[6:]
    fmt.Println("sli=",sli)
}

  如果需要修改字符串

//如果需要修改字符串,可以先将string -> []byte 或者 []rune -> 修改 -> 重写转成string
//"helloworld"改成"aelloworld"
arr := []byte(str)
arr[0] = 'a'
str = string(arr)
fmt.Println("str=",str)
//注意:转成[]byte后,可以处理英文和数字,但是不能处理中文
//因为[]byte是一个字节处理,而一个汉字是3个字节,会出现乱码
//解决方法是将string转成[]rune即可,因为[]rune是按字符处理,兼容汉字
arr2 := []rune(str)
arr2[0] = '你'
str = string(arr2)
fmt.Println("str=",str)

注意事项

  1. 切片初始化时,仍然不能越界。范围在[0-len(arr)]之间,但是可以动态追加。
  2. cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
  3. 切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用。
  4. 切片可以继续切片
sli2 := sli[1:2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是哈猿啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值