Go语言学习笔记(五)---数组和切片

4.6 数组与切片

4.6.1数组

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

数组的定义:var 数组名 [数组大小]数据类型

数组的内存布局:1)数组的地址可以通过数组名来获取&intArr。2)数组第一个元素的地址,就是数组的首地址。3)数组的各个元素的地址间距是依据数的类型决定的,比如int64->8、int32->4…

数组的使用:1)访问数组元素,直接数组名[下标]。

2)从终端输入数据保存到数组中

3)四种初始化数组的方式

	//四种初始化数组的方式
	var numArr1 [3]int = [3]int{1,2,3}
	var numArr2 = [3]int{4,5,6}
	var numArr3 = [...]int{8,9,10}
	var numArr4 = [...]int{0:11,1:12,2:13}
	var strArr5 = [...]string{1:"tom",2:"jack",0:"ross"}
	fmt.Println(numArr1,numArr2,numArr3,numArr4,strArr5)
	//终端输入数据保存到数组
	var score [5]float64
	for i := 0; i < len(score); i++ {
		fmt.Printf("请输入第%d个元素的值", i+1)
		fmt.Scanln(&score[i])
	}
	//打印数组
	for i := 0; i < len(score); i++ {
		fmt.Printf("score[%d]=%v", i, score[i])
	}

数组的遍历:

1)常规遍历len(arr)。2)for-range遍历:for index,value:= range arr{}

range函数中第一个返回值index是数组的下标,第二个value是在该下边位置的值,都是在for循环内部可见的局部变量,遍历时不想使用下标index,可以直接标为下划线_,index和value的名称可以自行设计。

数组细节:

1)数组是由多个相同类型数据的组合,一旦一个数组定义了,其长度是固定的,不能够再动态变化,且定义了int类型的数组就不能传入float类型的数据。

2)var arr []int表示可变长度的数组,此时arr就是一个slice切片

3)数组中的元素可以是任何数据类型,包括值类型和引用类型

4)数组创建后没有主动赋值,将使用默认值{0,“”,false}

5)数组步骤:声明数组开辟空间–>给数组各个元素赋值–>使用数组

6)数组下标是从0开始的,且下标必须再指定范围内,否则报panic:数组越界

7)数组属于值类型,在默认情况下是值传递,因此会进行值拷贝,数组间不会互相影响,而如果在其他函数中去修改原来的数组,可以使用引用传递。

func shuzu_test01(arr [3]int) {
	arr[0] = 80
	for _, value := range arr {
		fmt.Println(value)
	}
}

func shuzu_test02(arr *[3]int) {
	arr[0] = 80
	for _, value := range arr {
		fmt.Println(value)
	}
}

var arr = [...]int{1, 2, 3}
	shuzu_test01(arr)
	for _, value := range arr {
		fmt.Println(value)
	}
	shuzu_test02(&arr)
	for _, value := range arr {
		fmt.Println(value)
	}
	/*使用test01值传递,输出80,2,3   1,2,3*/
	/*使用test02引用传递,输出80,2,3   80,2,3*/

8)函数调用时需要注意传递的数组长度,不能把[3]int传递给[]int,也不能传递给[4]int

4.6.2 切片

切片slice 是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用类型的传递机制。

2)切片的使用和数组类似,遍历切片、访问切片元素和求切片长度len(slice)都一样。

3)切片的长度是可以变化的,因此切片是一个可以动态变化数组

4)切面定义的基本语法:var 切片名 []类型

切片在内存中形式:从底层而言就相当于一个结构体,包含ptr*、长度、和容量。可以发现&slice和&arr对应位置相同。修改slice的值,修改slice的值,其对应数组的值也会发生改变。

在这里插入图片描述

切片使用的三种方式:

1)定义一个切片,然后让切片引用一个已经创建好的数组。

2)通过make来创建切片,基本语法:var 切片名[]type = make([],len,[cap])其中type就是数据类型,len:大小,cap:指定切片容量【可选】

在这里插入图片描述

区别:通过make创建的slice只能由slice去访问,对外不可见。

3)定义一个切片,直接就指定具体的数组,类似make的方式。

	//方式一:声明/定义一个切片
	//slice就是切片名,int[1:3]表示slice引用到intArr这个数组
	slice := intArr[1:3] //引用intArr的起始下标为1,最后的下标为3(不包含3),表示[1,3)
	fmt.Println("原数组为", intArr)
	fmt.Println("切片元素为", slice)
	fmt.Println("slice的元素个数是", len(slice))
	fmt.Println("slice的容量是", cap(slice)) //切片的容量是可以动态变化的,数组没有容量的概念。
	/*原数组为 [1 22 33 66 99]
	切片元素为 [22 33]
	slice的元素个数是 2
	slice的容量是 4   */
	//slice在内存中的地址
	fmt.Printf("intArr[1]的地址是%p", &intArr[1])
	fmt.Printf("slice[0]的地址是%p,slice[0]的值为%v", &slice[0], slice[0])

	//方式二:通过make来创建切片,make:分配并初始化一个类型为切片,映射或者通道的对象,第一个参数为类型,第二个为size,容量必须>=长度
	var slice_2 []int = make([]int, 4, 10)
	// 省略cap表示为var slice_2 []int = make ([]int, 4 )
	//此时创建的数组,程序员只能通过slice查看,不能再通过数组查看
	fmt.Println(slice_2) //默认值为0

	//方式三:定义切片直接指定具体的数组
	var slice_3 []string = []string{"tom", "jake", "mary"}
	fmt.Println("切片元素为", slice_3)
	fmt.Println("slice的元素个数是", len(slice_3))
	fmt.Println("slice的容量是", cap(slice_3))

切片的遍历

遍历方式和数组一样有两种方式:

1)使用常规for循环遍历。2)for-range遍历

	//遍历切片
	for i := 0; i <= len(slice); i++ {
		fmt.Printf("%v", slice[i])
	}
	//for-range遍历
	for index, value := range slice {
		fmt.Printf("%v,%v", index, value)
	}

切片细节:

1)切片初始化时 var slice = arr[startindex:endindex]中,从arr数组下标为startIndex取到endindex但不包含endindex。

var slice = arr[0:end]简写成 arr[:end]

arr[start:len(arr)]简写成arr[start:]

arr[0:len(arr)]简写成arr[:]

2)切片初始化时,仍然不能越界【不能超过endindex】,但是可以动态增长。

3)cap是一个内置函数,用于统计切片的容量即最大可以存放多少个元素。切片定义完之后还不能直接使用【空指针】,需要让其引用到一个数组或者make一个空间供切片使用

4)切片可以继续切片。需要注意下标对应以及所有关联值的变化。

5)切片的动态增长,使用append内置增长:将元素追加到切片末尾。底层分析:切片append 操作的本质就是对数组扩容,go底层会创建新的数组newArr(安装扩容后大小),将slice原来包含的元素拷贝到新的数组newArr,slice重新引用到newArr,原数组销毁。注意newArr是在底层来维护的,程序员不可见。

var slice_1 []int = []int{100, 200, 300}
	//给slice_1追加400,500
	slice_1 = append(slice_1, 400, 500)
	//通过append追加slice3给slice3,...相当于解构,后面只能是切片不能够时数组。
	slice_1 = append(slice_1, slice_1...)

	var slice_2 []int = make([]int, 10)
	fmt.Println(slice_2)
	copy(slice_2, slice_1)
	fmt.Println(slice_2)

6)切片的拷贝操作,使用copy(para1,para2):将p2的元素拷贝给p1,其中p1,p2都是切片类型,两个切片都有自己的空间,因此数据互不影响。如果p2>p1则会将p1补满,不会发生错误。

7)在test(slice []int)函数中修改slice会直接影响实参(引用传递)

string 和slice:

1)string底层时byte数组,因此string也可以进行切片处理。可以说string的底层和切片类似。

在这里插入图片描述

2)string 本身不可变的,不能通过str[0]='z’来修改字符串。修改字符串需要先将string转换成[]byte切片或者[]rune,在进行修改,重写转成string

	str := "hello@xzc_11-17"
	str_slice := str[6:]
	fmt.Println(str_slice)
	//修改str中单个字符
	arr1 := []byte(str)//强转
	arr1[0] = 'z'
	str = string(arr1)//强转
	//由于[]byte是按照字节处理,因此不能够处理汉字,汉字是3个字节
	//汉字需要将 string 转成 []rune ,rune按照字符处理
	arr2 := []rune(str)
	arr2[2] = '北'
	str = string(arr2)

猜你喜欢

转载自blog.csdn.net/Destinyxzc/article/details/127912881