golang笔记04

本文介绍了Go语言中的数组和切片。数组是一种固定长度的数据序列,通过索引访问元素,并详细讲解了数组的语法、遍历、数据类型、冒泡排序以及多维数组。切片作为数组的抽象,其长度可变,提供了更灵活的操作,包括切片的创建、值传递和深浅拷贝等概念。

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

一、数组Array——复合类型

1、介绍

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串、或者自定义类型。数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为0,第二个索引为1,以此类推。数组的下标取值范围从0开始,到长度减1,数组大小一旦定义后,就不能改变。

2、数组的语法

声明和初始化数组
需要指名数组的大小和存储的数据类型
初始化数组中{}的元素个数不能大于[]中的个数
如果忽略[]中的数字不设置数组的大小,Go语言会根据元素的个数来设置数组的大小
未赋值时整型默认为0
var variable_name [size] variable_type
例子:
var balance [10] int32 
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}

其他创建方式
var a[4] int32var a =[4] int
var a=[4] int {1,2,3,4}
var a=[5]int{1:1,2:2}//指定位置赋值,其余位置为0,[0 1 2 0 0]
d :=[...]int{1,2,3} //根据元素的个数,设置数组的大小
c :=[5] int {1:2}//指定位置赋值,其余为0

例子:
package main
import "fmt"
func main() {
	var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
	balance1[1]=11
	var a=[5]int{1:1,2:2}
	fmt.Println(balance1[1])
	fmt.Println(len(balance1))//容器中实际存储的数据量
	fmt.Println(cap(balance1))//容器中能够存储的最大数量
	fmt.Println(a)
	//打印数组地址
	fmt.Printf("%p\n",&a[1])//打印某个元素地址
	fmt.Printf("%p",&a)//打印数组的初始地址
}

3、数组遍历for_range

//使用for循环遍历数组
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
	for i:=0;i<len(balance1);i++{
		fmt.Printf("%d ",balance1[i])
	}

//使用range遍历数组,同时适用于map slice array string,需要接收两个数值,下标和对应下标上的数值
//1、下标和数值同时需要获取
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
	for index,value:= range balance1{
		fmt.Printf("%d %d",index,value)
	}

//只需要值不需要下标
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
	for _,value:= range balance1{
		fmt.Printf("%d ",value)
	}

4、打印数组的数据类型

//数组和数值都是值传递

package main
import "fmt"
func main() {
	var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
	fmt.Printf("%T\n",balance1)
    
    //数组之间的比较,需要两个数组的大小和数据类型一样
    var a =[10]int32{1,2}
    fmt.Println(balance1==a)
}
输出结果:
[10]int32
false

5、值类型和引用类型

值类型:理解为存储的数值本身,将数据传递给其他的变量,传递的是数据副本(备份),int、float、string、bool、array

引用类型:理解为存储的数据的内存地址,slice,map…

6、数组的冒泡排序

//小到大排序
package main
import "fmt"
func main() {
	var balance1 = [10] int32{1,234,67,89,5,89,0,8,91,10}
	for i:=1;i<len(balance1);i++{
		for j:=0;j<len(balance1)-i;j++{
			if(balance1[j]>balance1[j+1]){
				balance1[j],balance1[j+1]=balance1[j+1],balance1[j]
			}
		}
	}
	fmt.Println(balance1)
}

//大到小排序
package main
import "fmt"
func main() {
	var balance1 = [10] int32{1,234,67,89,5,89,0,8,91,10}
	for i:=1;i<len(balance1);i++{
		for j:=0;j<len(balance1)-i;j++{
			if(balance1[j]<balance1[j+1]){
				balance1[j],balance1[j+1]=balance1[j+1],balance1[j]
			}
		}
	}
	fmt.Println(balance1)
}

7、多维数组

//存储的是一维的一维
//创建二维数组
例子:
var balance [10][1] int32 
var balance1 = [10][1] int32{{1},{2}}

其他创建方式
var a[4][1] int32var a =[4][1] int
var b=[4][1] int {{1}}
d :=[...][2]int{{1},{2}} 

例子:
package main
import "fmt"
func main() {
	var balance [10][1] int32
	var balance1 = [10][1] int32{{1},{2}}
	var a[4][1] int32
	var b=[4][1] int {{1}}
	d :=[...][2]int{{1},{2}}
	fmt.Println(balance,"\n",balance1,"\n",a,"\n",b,"\n",d)
}

//遍历二维数组
package main
import "fmt"
func main() {
	var balance [5][2] int
	//存值
	for i:=0;i<len(balance);i++{
		for j:=0;j<len(balance[0]);j++{
			balance[i][j]=j+1
		}
	}
	//取值
	for _,arr := range balance{
		for _,value := range arr{
			fmt.Print(value," ")
		}
	}
}

二、切片(Slice)

1、介绍

切片是对数组的抽象,与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大,切片是一种方便、灵活且强大的包装器。切片本身没有任何数据。它只是对现有数组的引用,切片与数组相比,不需要设置长度,在[]中不用设定值,相对来说比较自由

从概念上面来说切片像一个结构体,这个结构体包含了三个元素:

  1. 指针,指向数组中slice制定的开始位置
  2. 长度,即slice的长度
  3. 最大长度,也就是slice开始位置到数组的最后位置的长度

2、切片的语法

//定义切片,切片不需要说明长度,或使用make()来创建切片
//每个切片引用了一个底层数组
var 变量名 []数据类型
变量名 :=[] 数据类型
//第一个参数是类型slice、map、channel
//第二个参数:长度len
//第三个参数:容量cap,最多可以存储的元素数量
变量名 := make([]数据类型,len)
s1 := make([]int32,3,8)

例子
package main
import "fmt"
func main() {
	s1 := make([]int,3,8)
	fmt.Println(s1)
	s2 :=[] int {1,2,3,4,5}
	//切片末尾追加元素,就算超过最大容量也没事,会自动扩充(成倍增长,变为之前的两倍)
    //切片一旦扩容,就是重新指向一个新的底层数组
	for i:=1;i<=10;i++{
		s1=append(s1, i)
		fmt.Println(s1[i])
	}
	//将切片加入切片内
	s3 :=append(s2, s1...)
	fmt.Println(s3)

	///遍历切片
	for i:=0;i<11;i++{
		fmt.Print(s3[i]," ")
	}
	for _ ,slice :=range s3{
		fmt.Println(slice )
	}

	//切片扩容,切片扩容是重新指向一个新的底层数组
	fmt.Printf("%d %d\n",len(s3),cap(s3))//18 18
	s3=append(s3,1,2,3)
	fmt.Printf("%d %d\n",len(s3),cap(s3))//18 36
    
    //打印切片底层地址
    fmt.Printf("%p",s1)//打印底层地址,注意这里没有+&
}

3、在已有的数组上创建切片

package main

import "fmt"

func main() {
	/*
	在已有的数组上,直接创建切片,该切片的底层数组 就是当前的数组
	语法:
		slice :=arr[start:end]
		切片的中的数据:[start:end)
		arr[:end]从头到end
		arr[start:]从start到尾
	长度是start到end,容量是切片start到末尾
	*/
	a:=[10]int{1,2,3,4,5,6,7,8,9,10}
	s1:=a[:5]//取前五个数字 start0 末尾10 cap10
	s2:=a[5:]//取剩下的数字 start5 末尾10 cap5
	s3:=a[3:8]//取第四位到第七位 start3 末尾10 cap7
	s4:=a[:]//取全部 start0 末尾10 cap10
	fmt.Printf("s1地址:%p %d %d  ",s1,len(s1),cap(s1))
	fmt.Println(s1)
	fmt.Printf("s2地址:%p %d %d  ",s2,len(s2),cap(s2))
	fmt.Println(s2)
	fmt.Printf("s3地址:%p %d %d  ",s3,len(s3),cap(s3))
	fmt.Println(s3)
	fmt.Printf("s4地址:%p %d %d  ",s4,len(s4),cap(s4))
	fmt.Println(s4)

	//更改数组内容,切片如果包含此元素也会被修改
	fmt.Printf("------------------------------------------------------\n")
	a[5]=100
	fmt.Println(a) //{1,2,3,4,5,100,7,8,9,10}
	fmt.Println(s1)//[1 2 3 4 5]
	fmt.Println(s2)//[100 7 8 9 10]
	fmt.Println(s3)//[4 5 100 7 8]
	fmt.Println(s4)//[1 2 3 4 5 100 7 8 9 10]

	//更改数组内容,对数组切片进行增加数据,因为是同一个底层数组,
	//所以如果另一个切片和更改元素的切片与原数组对应的位置,则另一个数组也会同样被改变
	//当该切片扩容之后,就与原来的其他同一数组的切片无关系
	fmt.Printf("------------------------------------------------------\n")
	//[1 2 3 4 5] 5 10
	s1=append(s1,6,7,8)
	s1=append(s1,6,7,8)
	fmt.Println(a)//[1 2 3 4 5 6 7 8 9 10]
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println(s3)
	fmt.Println(s4)
	s1[1]=100;
	fmt.Println(s1)
	fmt.Println(s4)
}

4、值传递与地址传递

值传递:int、float、stringbool、array、传递的是数据副本
引用类型 Slice,传递的地址,多个变量指向了同一块内存地址,所以当该切片未进行扩容时都是属于同一个内存地址

5、深拷贝与浅拷贝

/*深拷贝:拷贝的是数据本身。
	值类型的数据,默认都是深拷贝:array,int,float,string,bool,struct

浅拷贝:拷贝的是数据地址。
	导致多个变量指向同一块内存
	引用类型的数据,默认都是浅拷贝:slice,map
	
func copy(接受拷贝的切片,被拷贝的切片)可以实现切片的深拷贝
*/

package main
import "fmt"
func main() {
	s1:=[] int{1,2,3,4}
	s2:=make([]int,0)//最大容量为0
	//将切片s1中的元素加入切片是s2
	for i:=0;i<len(s1);i++{
		s2=append(s2,s1[i])
	}
	fmt.Println(s1)// [1 2 3 4]
	fmt.Println(s2)//[1 2 3 4]
	/*
	此时改变切片s1元素不会造成切片s2对应元素改变,因为当切片进行扩容时就会重新指向一个新的底层地址
	所以在给切片s2赋值的时候,切片s2进行了扩容,使得切片s2所指向的底层地址和切片s1指向的底层地址不是同一个,
	故改变切片s1的值的时候,切片s2的值不变
	*/
	s1[0]=100
	fmt.Println(s1)//[100 2 3 4]
	fmt.Println(s2)//[1 2 3 4]

	//使用copy()对切片进行深拷贝
	/*
	copy(拷贝后切片a,被拷贝的切片b)
	拷贝后的切片大小和拷贝的内容的多少取决于a原本的大小,如果a(大小1)的大小小于b(大小2),a=b[:len(a)]
	如果a(大小3)的大小大于b(大小2),a接受b的全部元素,a的组成=b的全部元素+a未被b覆盖的元素,
	就是a=b+a[len(b):]
	*/
	s3:=[] int {7,8,9,10,11}//s2=[1 2 3 4]
	s4:=[]int {10}
	s5:=[]int {1,2}
	s6:=[]int {9,2}
	copy(s3,s2)//将s2的元素拷贝到s3对应的位置中
	copy(s4,s2)//将s2拷贝到s4中
	copy(s5,s2[2:])//将s3后三个元素(3,4,11)拷贝给s5
	copy(s6[1:],s2)//将s2拷贝给s6的第二个位置开始
	fmt.Println(s3)//[1 2 3 4 11]
	fmt.Println(s4)//[1]
	fmt.Println(s5)//[3 4]
	fmt.Println(s6)//[9 1]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

废材终结者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值