Dawn's Blogs

分享技术 记录成长

0%

GO语言圣经学习笔记 (2) 数组和切片

数组

特点

  • 数组长度固定,所以在声明阶段必须显示的声明数组长度
1
2
3
4
5
6
// 声明一个长度为10的int数组var 
a [10]int
// 数组的长度是根据初始化值的个数来计算
b := [...]int{1, 2, 3}
// 定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其它元素都是用0初始化
c := [...]int{99: -1}
  • 数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定

  • 同类型数组之间可以相互比较。如果一个数组的元素类型可以相互比较的,那么数组类型也是可以相互比较的,这时候我们可以直接通过==比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的

1
2
3
4
5
6
a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{1, 3}
fmt.Println(a == b, a == c, b == c) // "true false false"
d := [3]int{1, 2}
fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int

数组是值传递的

与其他遍程语言不同,当数组作为函数的参数时,默认是值传递的。

所以函数参数变量接收的是一个复制的副本,并不是原始调用的变量。因为函数参数传递的机制导致传递大的数组类型将是低效的,并且对数组参数的任何的修改都是发生在复制的数组上,并不能直接修改调用时原始的数组变量

可以显式地传入一个数组指针,那样的话函数通过指针对数组的任何修改都可以直接反馈到调用者

1
2
3
func zero(ptr *[32]byte) {
*ptr = [32]byte{}
}

缺陷

虽然通过指针来传递数组参数是高效的,而且也允许在函数内部修改数组的值,但是数组依然是僵化的类型:

  • 数组的类型包含了僵化的长度信息,长度不可改变

  • 没有任何添加或删除数组元素的方法

Slice切片

slice(切片)代表变长的序列,序列中每个元素都有相同的类型。

特点

  • slice底层引用了一个数组对象

  • 一个slice由三部分组成,像一个结构体:指针、长度和容量

    • 指针向第一个slice元素对应的底层数组元素的地址
    • 长度对应slice中元素的数目,长度不能超过容量
    • 容量一般是从slice的开始位置到底层数据的结尾位置
  • slice是引用传递,因为slice值包含指向第一个slice元素的指针,因此向函数传递slice将允许在函数内部修改底层数组的元素。换句话说,复制一个slice只是对底层的数组创建了一个新的slice别名

  • slice之间不能比较,因此我们不能使用==操作符来判断两个Slice是否含有全部相等元素。

    • 唯一合法的比较就是和nil比较。一个零值的slice等于nil。一个nil值的slice并没有底层数组
    • 一个nil值的slice的长度和容量都是0。但是也有nil值的slice的长度和容量也是0的,例如[]int{}
1
2
3
4
var s []int			// len(s) == 0, s == nil
s = nil // len(s) == 0, s == nil
s = []int(nil) // len(s) == 0, s == nil
s = []int{} // len(s) == 0, s != nil

make函数

1
func make(Type, size IntegerType) Type

内建函数make分配并初始化一个类型为切片、映射、或通道的对象。第一个参数Type是类型,并返回一个Type类型的对象,而不是*Type

1
2
3
// 分配一个[]int切片,len=5,cap=10
// a的类型是[]int,而不是*[]int
a := make([]int, 5, 10)

make和new

GO语言中,make和new的区别如下:

  • make只能用来分配slice、map、chan类型的数据;new可以分配任意类型的数据
  • make返回引用,即T;new返回的是指针,即*T
  • make不仅分配内存,还初始化了;new只分配内存,这段空间被清零

append函数

内置的append用于为slice追加元素,可以追加一个、多个元素,甚至追加一个slice

1
2
3
4
5
6
var x []int
x = append(x, 1)
x = append(x, 2, 3)
x = append(x, 4, 5, 6)
x = append(x, x...) // append the slice x
fmt.Println(x) // "[1 2 3 4 5 6 1 2 3 4 5 6]"

追加元素时

  • len > cap,会新申请一段2*cap的内存空间来存储slice
  • len <= cap,返回的slice的底层数组依然是传入slice的底层数组