go数组和切片
概述
go的数组和切片是很相似很容易混淆的概念。
数组Array
- 声明
var a [10] int var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} // 自动长度
- 是同一种数据类型的固定长度的序列。
- 数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
- 声明之后每个元素都会初始化其数值的默认值
- 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
- 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此在函数改变参数中数组的值,不会改变本身的值。
- 指针数组 [n]*T,数组指针 *[n]T。
- 支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
- 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
切片Slice
- 声明
// slice := make([]type, len, cap) var a = make([]int, 10) // 声明同时初始化 var aa := make([]int, 6) // 省略 cap,相当于 cap = len。 var aaa := []int{0, 1, 2, 3, 8: 100} // 通过初始化表达式构造,可使用索引号。 var b []int // 此时是nil var c = []int {1,2,3} var arr [10]int var d = arr[:] // 从数组切片 var f = arr[1:2] // 前包后不包,实际只有一个值
- 类似java的List,它通过内部指针和相关属性引用数组片段,以实现变长方案。
- 切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
- 切片的长度是其内部的属性,因此可以改变,简单理解切片是一个可变的数组。
- 如果 slice == nil,那么 len、cap 结果都等于 0。
数组的range遍历是值传递的
type A struct {
a string
}
arr := [...]A {
{a:"1"},
{a:"2"},
}
fmt.Println(arr)
for i, v := range arr {
// range 是值传递,因此无法直接修改对象的值,对比地址的结果是false
fmt.Println(&v == &arr[i])
v.a = "3"
}
fmt.Println(arr)
// 通过索引修改没有问题
for i,_ := range arr {
arr[i].a = "3"
}
fmt.Println(arr)
// 指针数组,遍历的是指针,如此修改值生效
arr2 := [...]*A {
{a:"1"},
{a:"2"},
}
fmt.Println(*arr2[0], *arr2[1])
for i, v := range arr2 {
fmt.Println(v == arr2[i])
v.a = "3"
}
fmt.Println(*arr2[0], *arr2[1])
结果:
[{1} {2}]
false
false
[{1} {2}]
[{3} {3}]
{1} {2}
true
true
{3} {3}
数组的指针传递的深度理解
package main
import (
"fmt"
)
// 数组是值传递的
func changeArr(a [10]int,index int, val int) {
a[index] = val
}
// 修改数组指针,生效
func changeArrPtr(a *[10]int,index int, val int) {
a[index] = val
}
// 修改
func changePtrArr(a [1]*int,index int, val *int) {
a[index] = val
}
func changePtrArrPtr(a *[1]*int,index int, val *int) {
a[index] = val
}
func main() {
arr := [10]int{1, 2, 3, 4, 5}
// 修改数组,生效,2
arr[0] = 2
fmt.Println(arr[0])
arr[0] = 1
// 值传递修改数组,不生效,1
changeArr(arr, 0, 2)
fmt.Println(arr[0])
// 指针传递修改数组,生效,2
changeArrPtr(&arr, 0, 2)
fmt.Println(arr[0])
arr[0] = 1
// 指针数组
var a = 1
var b = 2
var c = &b
arrptr := [1]*int{&a}
// 同样是值传递,因此不生效,1
changePtrArr(arrptr, 0, c)
fmt.Println(*arrptr[0])
// 指针传递,生效,2
changePtrArrPtr(&arrptr, 0, c)
fmt.Println(*arrptr[0])
}
结果:
2
1
2
1
2
切片复制时并不会改变内存地址
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[0:2]
s2 := []int{66, 66, 66}
copy(s1, s2)
fmt.Println(s1, arr)
结果:
[66 66] [66 66 3 4 5]
切片的切片
arr := [5]int{1,2,3,4,5}
s1 := arr[1:2]
s2 := s1[0:3]
fmt.Println(s2)
结果:
[2 3 4]
切片的内存分配深度理解
package main
import (
"fmt"
)
// 切片是引用传递的
func changeSplit(s []int, index int, val int) {
s[index] = val
}
func main() {
arr := [10]int{1, 2, 3, 4, 5}
s1 := arr[:2:5]
// cap=5 len=2 val=1,2
fmt.Println(cap(s1), len(s1), s1)
// 修改切片的值,会同步修改其指向的数组的值!!!
// 此处修改0索引的值为5,数组的0索引也同步的改为了5
// 因此对数组进行切片,并不会复制数组,而是用在指针指向数组
s1[0] = 5
fmt.Println(s1, arr)
// 通过函数传递的是切片的引用,因此在函数内修改切片也会同样的修改原数组!!!
// 此处修改0的索引值为1,数组的0索引也同步的改为了1
changeSplit(s1, 0, 1)
fmt.Println(s1, arr)
s1 = append(s1, 123)
// 未超出容量,append 修改切片也会同步的修改原数组,这也是因为切片底层有对数组的指针
fmt.Println(s1, arr)
// 两个指针是一样的
fmt.Println(&s1[0], &arr[0], &arr[0]==&s1[0])
// 起始;终止;容量
s2 := arr[0:2:2]
fmt.Println(cap(s2), len(s2), s2)
// 修改另一个切片,同时会修改第一个切片,因为其底层数组是一样的
s2[0] = 666
// 都是666
fmt.Println(s2[0], s1[0], arr[0])
// true
fmt.Println(&s1[0] == &s2[0])
s2 = append(s2, 233)
// 超出容量(2)重新分配切片空间,此时指向的已经不是原数组,因此修改切片不会修改原数组
fmt.Println(s2, arr)
// 指针已经不一样
fmt.Println(&s2[0], &arr[0], &arr[0]==&s2[0])
}
结果:
5 2 [1 2]
[5 2] [5 2 3 4 5 0 0 0 0 0]
[1 2] [1 2 3 4 5 0 0 0 0 0]
[1 2 123] [1 2 123 4 5 0 0 0 0 0]
0xc00001a140 0xc00001a140 true
2 2 [1 2]
666 666 666
true
[666 2 233] [666 2 123 4 5 0 0 0 0 0]
0xc000012240 0xc00001a140 false
字符串切片
str := "你好,golang"
// 默认是byte[]
for i := 0; i < len(str); i++ {
fmt.Printf("%c(%v)", str[i], str[i])
}
fmt.Println()
// 使用rune遍历
runes := []rune(str)
for i := 0; i < len(runes); i++ {
fmt.Printf("%c(%v)", runes[i], runes[i])
}
fmt.Println()
// 直接用range遍历
for _,v := range str {
fmt.Printf("%c(%v)", v, v)
}
fmt.Println()
// 默认切片采用的时byte[],因此存在问题
hello := str[0:2]
fmt.Println(hello)
// 转成rune切片
hello2 := ([]rune(str))[0:2]
fmt.Println(string(hello2))
结果:
ä(228)½(189) (160)å(229)¥(165)½(189)ï(239)¼(188)(140)g(103)o(111)l(108)a(97)n(110)g(103)
你(20320)好(22909),(65292)g(103)o(111)l(108)a(97)n(110)g(103)
你(20320)好(22909),(65292)g(103)o(111)l(108)a(97)n(110)g(103)
�
你好
版权声明
本文章由作者“衡于墨”创作,转载请注明出处,未经允许禁止用于商业用途
发布时间:2021年03月08日 06:10:54
备案号:
闽ICP备19015193号-1
关闭特效
评论区#
还没有评论哦,期待您的评论!
引用发言