Go xmas2020 学习笔记 10、Slices in Detail

课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)

主讲老师 Matt Holiday

image-20220401081031592

10-Slices in Detail

Slice

package main

import "fmt"

func main() {
	var s []int

	t := []int{}
	u := make([]int, 5)
	v := make([]int, 0, 5)

	fmt.Printf("%d, %d, %T, %5t %#[3]v\n", len(s), cap(s), s, s == nil)
	fmt.Printf("%d, %d, %T, %5t %#[3]v\n", len(t), cap(t), t, t == nil)
	fmt.Printf("%d, %d, %T, %5t %#[3]v\n", len(u), cap(u), u, u == nil)
	fmt.Printf("%d, %d, %T, %5t %#[3]v\n", len(v), cap(v), v, v == nil)

}
0, 0, []int,  true []int(nil)
0, 0, []int, false []int{}
5, 5, []int, false []int{0, 0, 0, 0, 0}
0, 5, []int, false []int{}

image-20220405134013956

\(t\) 中的 \(addr\) 指向一个起哨兵作用的结构,所以我们知道它是空的而不是 \(nil\).

可以用 append 方法生成元素存储地址,并返回一个描述符引用这个存储给 \(s\) . 即便 \(s\)\(nil\)


Empty vs nil slice

image-20220405134702334

使用 \(nil\)\(empty\) 映射替换切片在这个例子中分别是 null、{ } 。


判断切片是否为空不能使用 a == nil ,因为有 \(nil\)\(empty\) 两种情况,应该用 len(a) 进行判断。

最好make切片的时候给定 length,否则新建同长度容量的切片用append会将元素追加在一堆0的后面。


Important

package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	b := a[:1]

	fmt.Println("a = ", a)
	fmt.Println("b = ", b)

	c := b[0:2]
	fmt.Println("c = ", c)
	fmt.Println(len(b))
	fmt.Println(cap(b))
	fmt.Println(len(c))
	fmt.Println(cap(c))

	d := a[0:1:1]
	// e := d[0:2]
	fmt.Println("d = ", d)
	// fmt.Println("e = ", e) Error
	fmt.Println(len(d))
	fmt.Println(cap(d))
}
a =  [1 2 3]
b =  [1]
c =  [1 2]
1
3
2
3
d =  [1]
1
2

对截取的切片再次进行切片是根据原先的底层数组来的。

如果你使用两个索引切片符,你得到的切片的容量等于底层数组的容量。


package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	b := a[:1]
	// c := b[0:2]
	c := b[0:2:2]

	fmt.Printf("a[%p] = %v\n", &a, a)
	fmt.Printf("b[%p] = %v\n", b, b)
	fmt.Printf("c[%p] = %v\n", c, c)
    
	c = append(c, 5)
	fmt.Printf("a[%p] = %v\n", &a, a)
	fmt.Printf("c[%p] = %v\n", c, c)

	c[0] = 9
	fmt.Printf("a[%p] = %v\n", &a, a)
	fmt.Printf("c[%p] = %v\n", c, c)
}
a[0xc000010150] = [1 2 3]
b[0xc000010150] = [1]
c[0xc000010150] = [1 2]
a[0xc000010150] = [1 2 3]
c[0xc00000e2a0] = [1 2 5]
a[0xc000010150] = [1 2 3]
c[0xc00000e2a0] = [9 2 5]

\(a\) 是一个数组,\(b、c\) 是两个切片,它们指向 \(a\)

  • \(c\) 添加元素,会发现 \(a、c\) 被改变。\(c\) 的容量为 \(3\),长度为 \(2\),对 \(c\) 添加元素的时候把 \(a\) 修改了,覆盖 \(a\) 的第三个值。

  • \(c\) 限制容量数量,再添加元素会导致没有地方放置,所以会重新分配一块容量更大的内存区域,拷贝原先的元素,再把新加的元素放进去,底层数组地址发生改变

去掉 \(a\),将 \(b\) 声明为切片并初始化,\(b\) 描述符指向无命名的底层数组。用 \(c\) 对其切片,并添加元素,结果和上面是一样的。切片实际上是一些底层数组的别名。

posted @ 2022-04-05 14:34  小能日记  阅读(23)  评论(0编辑  收藏  举报