源码

go slice 的源码位于 go/src/runtime/slice.go 下

定义

slice 的定义如下

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

其中包括一个指向内存实际存储空间的指针 array
slice 的长度 len
slice 的容量 cap

扩容

主要的扩容逻辑是这一段代码

    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        if old.len < 1024 {
            newcap = doublecap
        } else {
            // Check 0 < newcap to detect overflow
            // and prevent an infinite loop.
            for 0 < newcap && newcap < cap {
                newcap += newcap / 4
            }
            // Set newcap to the requested cap when
            // the newcap calculation overflowed.
            if newcap <= 0 {
                newcap = cap
            }
        }
    }

可以看到 newcap 就是最终需要扩容的大小,cap 是我们程序所需要的容量
首先将 newcap 设置为原来容量的 2 倍
如果所需要的容量 cap 大于原来容量的 2 倍,直接把 newcap 设置为 cap
否则进入 else,将原 slice 长度与 1024 做比较,小于 1024,将 newcap 设置为原来容量的 2 倍
如果大于1024,就将容量增加原来容量大小的 1/4
这里也是直接使用了数字 1024,没有什么宏定义啊。。。也是写的很随心所欲啊

slice 使用中的一些小坑

看下面两端代码

func main() {
    var a [5]int = [5]int{
        1, 2, 3, 4, 5,
    }   
    var b = a[0:2]
    b = append(b, 1, 1, 1)
    fmt.Println(a, b)
}

打印结果 [1 2 1 1 1] [1 2 1 1 1]

func main() {
    var a [5]int = [5]int{
        1, 2, 3, 4, 5,
    }   
    var b = a[0:2]
    b = append(b, 1, 1, 1, 1)
    fmt.Println(a, b)
}

打印结果 [1 2 3 4 5] [1 2 1 1 1 1]

b 是取自于数组 a 的 slice, 我们往 b 中 append 数据
当一次数据量超过 a 的容量的时候,会给 b 重新分配一段内存, a 的数据不会变化
当一次数据量不超过 b 的容量的时候,就不会重新分配内存,a 的数据就会随着 b 而变化