Go 语言中 slice 是一种构建在 array 数组类型上的抽象类型,所以不管是从 array、slice 或者使用 make 构建 slice ,每个 slice 都是其底层数组的一个映射。一个 slice 由三部分组成,分别为 ptr(pointer)指向底层数组的指针、len(length)长度、cap(capacity)容量,下面来详细说明这三个部分。

    slices-Page-2.drawio (1).png

    ptr:既然 ptr 是指向底层数组的指针,那么指向数组的什么位置呢?创建 slice 时需要指定一个左闭右开(包含左边界,不包含右边界)的区间,slice 创建之后,观察代码中 L9 输出以及上图,不难发现 ptr 会指向区间左边界对应的数组元素位置。

    1. package main
    2. import "fmt"
    3. func main() {
    4. array := [...]int{ 0, 1, 2, 3, 4, 5, 6, 7 }
    5. sliceA := array[1:6]
    6. fmt.Printf("sliceA addr: %p, array[1] addr: %p;\n", sliceA, &array[1])
    7. }
    8. // L9: sliceA addr: 0xc00018a008, array[1] addr: 0xc00018a008;

    len:与 array 相同,len 代表 slice 当前元素的数量,如果 slice 声明为 slice := array[n:m],那么 len 就等于 m - n 。另外一点与 array 相同的时,slice 也可以使用 index 来访问内容,但也要注意,如果 index < 0 或者 >= len(slice),也会出现越界。

    cap:slice 的 len 可以扩容到到的最大值,值为 ptr 指向的 array 元素位置到 array 末尾的元素数量,如果 prt 指向 array 第 n 个元素,那么 cap = len(array) - n 。

    1. package main
    2. import "fmt"
    3. func main() {
    4. array := [...]int{ 0, 1, 2, 3, 4, 5, 6, 7 }
    5. sliceA := array[1:6]
    6. fmt.Printf("sliceA addr: %p, array[1] addr: %p;\n", sliceA, &array[1])
    7. fmt.Printf("sliceA: %d, len: %d, cap: %d;\n", sliceA, len(sliceA), cap(sliceA))
    8. }
    9. // L9: sliceA addr: 0xc00018a008, array[1] addr: 0xc00018a008;
    10. // L10: sliceA: [1 2 3 4 5], len: 5, cap: 7;

    需要注意的是,如果从一个 sliceA 构建 sliceB,那么 sliceB 的 ptr 也会指向 sliceA 的 ptr 指向的 array。因为 slice 本质上只是一个指向 array 的 pointer,所以理论上基于一个 array 可以构建任意多个 slice。

    slices-Page-1.drawio.svg

    如上图所示,虽然一个 array 上可以构建多个 slice,但是也正因为这些 slice 共用一个底层 array,并且 slice 修改时会修改底层 array,所以在这个底层 array 之上,其它映射了相同区间的 slice 也会受到影响。

    1. package main
    2. import "fmt"
    3. func main() {
    4. array := [...]int{ 0, 1, 2, 3, 4, 5, 6, 7 }
    5. sliceA := array[1:6]
    6. fmt.Printf("sliceA addr: %p, array[1] addr: %p;\n", sliceA, &array[1])
    7. fmt.Printf("sliceA: %d, len: %d, cap: %d;\n", sliceA, len(sliceA), cap(sliceA))
    8. sliceB := sliceA[:cap(sliceA)]
    9. fmt.Printf("sliceB: %d, len: %d, cap: %d;\n", sliceB, len(sliceB), cap(sliceB))
    10. sliceC := sliceB[2:4]
    11. fmt.Printf("sliceC: %d, len: %d, cap: %d;\n", sliceC, len(sliceC), cap(sliceC))
    12. sliceC[1] = 100
    13. fmt.Printf("sliceC: %d, len: %d, cap: %d;\n", sliceC, len(sliceC), cap(sliceC))
    14. fmt.Printf("array: %d, len: %d, cap: %d;\n", array, len(array), cap(array))
    15. fmt.Printf("sliceA: %d, len: %d, cap: %d;\n", sliceA, len(sliceA), cap(sliceA))
    16. fmt.Printf("sliceB: %d, len: %d, cap: %d;\n", sliceB, len(sliceB), cap(sliceB))
    17. }
    18. // L9: sliceA addr: 0xc000102008, array[1] addr: 0xc000102008;
    19. // L10: sliceA: [1 2 3 4 5], len: 5, cap: 7;
    20. // L14: sliceB: [1 2 3 4 5 6 7], len: 7, cap: 7;
    21. // L18: sliceC: [3 4], len: 2, cap: 5;
    22. // L22: sliceC: [3 100], len: 2, cap: 5;
    23. // L23: array: [0 1 2 3 100 5 6 7], len: 8, cap: 8;
    24. // L24: sliceA: [1 2 3 100 5], len: 5, cap: 7;
    25. // L25: sliceB: [1 2 3 100 5 6 7], len: 7, cap: 7;