Go 数组与切片
#### Go 数组与切片
***渡风渡尘缘,修行亦修心***
##### 数组
数组是可以存放多个同一类型的数据的集合,数组也是一种数据类型,它是值类型;
数组如何定义?
var 数组名 [数组大小]数据类型: var a [5]int
package main
import "fmt"
func main(){
var intArr [3]int
intArr[0] = 10
intArr[1] = 20
intArr[2] = 30
fmt.Println(intArr)
}
数组在内存中的布局:

1. 数组的地址可以通过数组名获取: &intArr;
2. 数组的第一个元素地址就是数组的地址:;
3. 数组的各个元素的地址间隔根据数组的类型决定: int64-> 8 字节,int32-> 4字节;
4. 数组如果未给元素赋值,元素的默认值为类型的零值: int64->0,string->"",float64->0;
5. 访问数组中的元素: 使用下标索引方式(左闭右开方式),这个与其它语言一样,超出下标索引将会出错,索引越界;
6. 数组的遍历有两种方式: for 传统方式; for range 方式 ;
package main
import "fmt"
func main(){
var a [5]int
a[0] = 1
a[1] = 2
a[2] = 3
// 默认值: a[3] = 0 , a[4] = 0
// 下标越界: a[5] = 10 // 错误, 下标越界
fmt.Println(a) //[1 2 3 0 0 ]
fmt.Printf("a address is %p\n",&a) // 0xc000078030
fmt.Printf("a[0] address is %p\n",&a[0]) //0xc000078030
fmt.Printf("a[1] address is %p\n",&a[1]) //0xc000078038
fmt.Printf("a[2] address is %p\n",&a[2]) //0xc000078040
// 数组的遍历方式
for i:=0;i<len(a);i++{
fmt.Println(a[i])
}
// for range 方式
for k,v := range a {
fmt.Println(a[k])
fmt.Println(v) // 这个值是元素的副本, 所以不建议在v 上操作: 取址或赋值;
}
}
---
数组有多种初始化方式:
import "fmt"
func main(){
// 1. 字面量直接赋值
var a [5]int = [5]int{1,2,3,4,5}
var b = [5]int{6,7,8,9,0}
// 2. 数组长度由编译器推导
var c = [...]int{1,2,3,4,5,6}
// 3. 根据索引位置直接赋值
var d = [...]int{1:1,10:0,3:3}
// 4. 类型推导
f := [...]int{1:1,9:9,10:0}
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
fmt.Println(f)
}
---
数组使用注意事项
1. 数组是多个相同类型的集合,一旦声明或者定义了,数组的长度将是固定不变的,不能动态变化 ;
2. 数组中的元素可以是任意类型,可以是值类型,也可以是引用类型,不可以混用;
3. 数组创建后如果不赋值,将采用类型的零值 ;
4. 数组属于值类型, 在默认情况下是传递数组是值传递,所以数组之间不会影响;
5. 如果需要更改原数组,需要传递数组的地址(类似引用传递);
6. 长度是数组类型的一部分,在传递参数时需要注意数组的长度;
package main
import "fmt"
func test01(a [5]int){
a[1] = 20
fmt.Println(a) //[1 20 3 4 5]
}
func test02(a *[5]int){
a[1] = 20
fmt.Println(*a) // [1 20 3 4 5]
}
func main(){
var a = [5]int{1,2,3,4,5}
test01(a)
fmt.Println(a) // [1 2 3 4 5]
// 如果需要修改原数组的值,需要传递数组的地址
test02(&a)
fmt.Println(a) // [1 20 3 4 5]
// 传递参数时需要考虑数组的长度
//var b = [3]int{1,2,3}
//test01(b) // 错误, 数组的长度不正确
}
在Go 编程中,数组的使用还是不太多, 因为不能动态的扩容,数组一般作为已经确定的数据使用;
##### 切片
1. 切片是数组的一个引用, 所以切片属于引用类型,在进行参数传递时,属于引用传递;
2. 切片的使用与数组类似: 遍历,访问等;
3. 切片的长度是可以动态变化的,所以也可以说切片是动态变化的数组;
4. 切片的基本语法 :
var 切片名 []类型
package main
import "fmt"
func main(){
// 这里只演示切片如何使用,稍后再介绍切片如何声明与赋值操作
// 声明并定义一个数组
var a = [5]int{1,2,3,4,5}
// 切片s 引用已经存在的数组a
var s = a[:] // 这种方式引用所有的数组元素,也可引用一部分数组元素 var s = a[1:3]
fmt.Printf("%T\n",s)
fmt.Println(s)
fmt.Println(len(s)) // 5 切片的长度
fmt.Println(cap(s)) // 5 切片的容量,容量和长度是可以动态变化;
}
---
切片在内存中的布局

1. slice 是一下引用类型;
2. 从底层上讲,slice 实际上就是一个结构体(稍后会学习到)
type slice struct {
ptr *[2]int
len int
cap int
}
切片的初始化
package main
import "fmt"
func main(){
// 方式1 创建一个数组,让切片引用这个数组
var a = [5]int{1,2,3,4,5}
var s = a[:]
// 同数组一样, 在赋值或取值时,仍然需要注意切片下标索引不可越界
//s[10] = 0
s[4] = 50
fmt.Println(s)
// 方式2
// 切片是引用类型,所以声明后需要make 分配内存
var b []int
// make 分配内存,长度可以指定任意值,建议根据要存储的数据长度合适定义
// make 参数,还可以定义切片的容量,如果定义了切片的容量,则要求容量大于切片的长度
b = make([]int,5)
fmt.Println(b)
// 方式3
// 字面量方式,声明后直接赋值
var c = []int{1,2,3,4,5}
fmt.Println(c)
}
1. 方式1: 直接引用已经存在的数组,开发者对底层是可见的;
2. 方式2: 通过make 创建的切片,程序也会创建一个数组,不过这个数组对开发者不可见,由程序自己维护;
3. 方式3: 类似make;
---
切片使用的注意事项:
1. 切片的遍历与访问同数组一样, 这里就不写了;
2. 切片定义后,不能直接使用,需要引用一个数组或make 分配内存给切片使用;
3. 使用append 函数可以对切片进行动态扩容;
4. 使用copy 函数可以对切片进行拷贝,拷贝后的切片是独立的, 在新的切片上操作对原切片没有影响;
5. 切片是引用类型,在给函数传递参数(切片)时,属于引用传递;
package main
import "fmt"
func test01(a []int){
a[0] = 100
fmt.Println(a)
}
func main(){
var a = []int{1,2,3}
fmt.Println(a)
// 使用append 函数将切片扩容
a = append(a,4)
fmt.Println(a)
// 使用copy 函数进行切片复制
// 在使用copy 函数时需要注意以下几点:
//1. 目标(dst)必须是已经声明并初始化过的
//2. 如果目标的长度小于被复制的切片长度,则以目标的长度为准复制,超出部分不复制;
var b []int
b = make([]int,3)// 超出元素4,将不会被复制
copy(b,a)
fmt.Println(b)
// 对新切片的操作不会影响到原来的切片
b[1] = 10
fmt.Println(a)
fmt.Println(b)
// 切片属于引用传递
test01(a)
fmt.Println(a)
}
---
string 与slice
在学习基本数据类型时我们学习了string 类型,知道它是由多个字节连接在一起的字符串;
实际上string 底层是一个字节数组,所以string 也可以进行切片操作;
但是string 是不可变的, 所以不能string[0]='x', 这种赋值操作; 如果需要更改,需要将string 转为[]byte 切片,更改完成后再转为string 类型;
package main
import "fmt"
func main(){
var a = "abcdef"
// 可以进行切片处理
fmt.Println(a[:3])
// 不可以更改string 的值
// a[1] = 'f' //这是错误的
// 需要将string 转为 []byte 切片才可以操作
var arr []byte
arr = []byte(a)
arr[1] = 'f'
a = string(arr) // 更改完成后再将切片转为string 类型
fmt.Println(a)
// []byte 只能处理英文与数字,处理其它语言时需要将string 转为[]rune 切片
var b = "你好啊"
var arr2 []rune
arr2 = []rune(b)
arr2[0] = '我'
b = string(arr2)
fmt.Println(b)
}
个人微信公众号上有最新的文章,欢迎大家关注,一同交流学习

每天进步一点点!加油

浙公网安备 33010602011771号