数组与切片

以前粗学时并没有留意 Golang 的数组,一直以为只有切片。

今天看《Go 语言实践》时才发现 Golang 也有数组。与切片的区别是定义时指定长度(例如长度为 4 的 int 型数组var array [4]int)。

数组赋值时会拷贝整个数组,所以作为参数传递时也会完整拷贝,要考虑性能合理使用,必要时可以使用指针。

以下代码便于理解数组和切片:

package main

import "fmt"

func main() {
// 定义数组 a1
a1 := [4]int{1, 2, 3, 4}

    // 数组 a2 来自 a1 的拷贝
    a2 := a1

    var a3 [4]int
    a3 = a1 // 同样会完整拷贝

    // a2 修改自己的内存区域
    a2[0] = 0

    // 切片 s1 来自 a1,其空间仍由 a1 实现
    s1 := a1[1:2]
    // 修改切片 s1 会影响数组 a1
    s1 = append(s1, -1, -2)

    fmt.Println("a1:", a1)
    fmt.Println("a2:", a2)
    fmt.Println("a3:", a3)
    fmt.Println("s1:", s1)

}

输出:

a1: [1 2 -1 -2]
a2: [0 2 3 4]
a3: [1 2 3 4]
s1: [2 -1 -2]

Slice 保存的是长度、容量以及其底层数组的地址。

这里 s1 来自 a1 的切片,底层数组与 a1 同内存区域,所以对 s1 的 append 导致 a1 的数值被修改。

在使用中也要注意这种情况,合理使用。

使用三个索引创建切片

这种格式为:slice[i:j:k]的切片方式,前两个索引与原来相同,第三索引用于表示新切片的容量。但它不是直接表示容量大小,而是表示原切片或者数组的索引位置。当它越界时会出现运行时错误。

所以新切片的长度和容量应该这样计算:

长度:j - i
容量:k - i
func main() {
a1 := [4]int{1, 2, 3, 4}
// s1 来自 a1
s1 := a1[1:2:3]

    // 修改a[1]
    a1[1] = 0

    fmt.Println("a1:", a1)
    fmt.Println("s1:", s1)

    fmt.Println("-------------")

    // append s1
    s1 = append(s1, -1)
    s1 = append(s1, -2)

    fmt.Println("a1:", a1)
    fmt.Println("s1:", s1)

}

输出:

a1: [1 0 3 4]
s1: [0] // <-对`a[1]`的修改也影响了 s1

---

a1: [1 0 -1 4]
s1: [0 -1 -2]

使用slice[i:j:k]创建切片的时候,同样没有立即创建新的底层数组,只会在append导致len > cap扩容时才创建新数组。