1. 切片声明时默认的长度和容量

1.1 切片声明时不指定容量

make
func main() {
	s1 := make([]int, 3)
	fmt.Println("s1 length: ", len(s1))   // s1 length:  3
	fmt.Println("s1 capacity: ", cap(s1)) // s1 capacity:  3
	fmt.Printf("s1 value: %#v\n", s1)     // s1 value: []int{0, 0, 0}
}

由上面可以看出来,当只声明切片的长度时,容量是和长度保持一致的。

1.2 切片声明时指定容量

而在声明切片时,如果指定切片的容量,那么容量就为指定的值

func main() {
	s2 := make([]int, 3, 7)
	fmt.Println("s2 length: ", len(s2))   // s2 length:  3
	fmt.Println("s2 capacity: ", cap(s2)) // s2 capacity:  7
	fmt.Printf("s2 value:  %#v\n", s2)    // s2 value:  []int{0, 0, 0}
}
2. 指定切片区间作为新切片时,新切片的长度和容量

看下面代码

func main() {
	s3 := []int{1, 2, 3, 4, 5, 6, 7, 8}
	s4 := s3[3:6]
	fmt.Printf("s4 length is: %d\n", len(s4))   // s4 length is: 3
	fmt.Printf("s4 capacity is: %d\n", cap(s4)) // s4 capacity is: 5
	fmt.Printf("s4 value is: %d\n", s4)         // s4 value is: [4 5 6]

	s5 := s4[:cap(s4)]
	fmt.Printf("s5 length is: %d\n", len(s5))   // s5 length is: 5
	fmt.Printf("s5 capacity is: %d\n", cap(s5)) // s5 capacity is: 5
	fmt.Printf("s5 value is: %d\n", s5)         // s5 value is: [4 5 6 7 8]
}
s4s3s4s4cap(s4)=8-3=5s4s3
cap(s4)=5s4[:cap(s4)]s4[:5]s4s3s4[:5]s3s5=[4 5 6 7 8]
3. 切片扩容时容量的变化
append
1024210241.25
func main() {
	s6 := make([]int, 0)
	fmt.Printf("The capacity of s6: %d\n", cap(s6))
	for i := 1; i <= 10; i++ {
		s6 = append(s6, i)
		fmt.Printf("s6(%d): len: %d, cap: %d, address %p\n", i, len(s6), cap(s6), s6)
	}
	fmt.Println()
}

输出结果如下:

The capacity of s6: 0
s6(1): len: 1, cap: 1, address 0xc0000160e8
s6(2): len: 2, cap: 2, address 0xc000016130
s6(3): len: 3, cap: 4, address 0xc0000145c0
s6(4): len: 4, cap: 4, address 0xc0000145c0
s6(5): len: 5, cap: 8, address 0xc00001c200
s6(6): len: 6, cap: 8, address 0xc00001c200
s6(7): len: 7, cap: 8, address 0xc00001c200
s6(8): len: 8, cap: 8, address 0xc00001c200
s6(9): len: 9, cap: 16, address 0xc00007a080
s6(10): len: 10, cap: 16, address 0xc00007a080

通过上面代码我们可以得知:

appendappend
slicegrowslice
func growslice(et *_type, old slice, cap int) slice {
    // ……
    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        if old.len < 1024 {
            newcap = doublecap
        } else {
            for newcap < cap {
                newcap += newcap / 4
            }
        }
    }
    // ……
    capmem = roundupsize(uintptr(newcap) * ptrSize)
    newcap = int(capmem / ptrSize)
newcap

如下:

func main() {
	s7 := make([]int, 1024)
	fmt.Printf("The capacity of s7: %d\n", cap(s7))
	s7e1 := append(s7, make([]int, 200)...)
	fmt.Printf("s7e1: len: %d, cap: %d\n", len(s7e1), cap(s7e1))
	s7e2 := append(s7, make([]int, 400)...)
	fmt.Printf("s7e2: len: %d, cap: %d\n", len(s7e2), cap(s7e2))
	s7e3 := append(s7, make([]int, 600)...)
	fmt.Printf("s7e3: len: %d, cap: %d\n", len(s7e3), cap(s7e3))
	fmt.Println()
}

最终结果为:

The capacity of s7: 1024
s7e1: len: 1224, cap: 1280
s7e2: len: 1424, cap: 1696
s7e3: len: 1624, cap: 2048
4. 多个切片指向同一个数组,对切片元素的修改会影响到整个数组

如果多个切片指向同一底层数组,引用相同片段的底层数组会因为其中一个切片的改变影响整个底层数组, 因此需要特别注意。

func main() {
	a1 := [7]int{1, 2, 3, 4, 5, 6, 7}
	fmt.Printf("a1: %v (len: %d, cap: %d)\n", a1, len(a1), cap(a1))
	s9 := a1[1:4]

	fmt.Printf("s9: %v (len: %d, cap: %d)\n", s9, len(s9), cap(s9))
	for i := 1; i <= 5; i++ {
		s9 = append(s9, i)
		fmt.Printf("s9(%d): %v (len: %d, cap: %d)\n", i, s9, len(s9), cap(s9))
	}
	fmt.Printf("a1: %v (len: %d, cap: %d)\n", a1, len(a1), cap(a1))
}

输出结果为:

a1: [1 2 3 4 5 6 7] (len: 7, cap: 7)
s9: [2 3 4] (len: 3, cap: 6)
s9(1): [2 3 4 1] (len: 4, cap: 6)
s9(2): [2 3 4 1 2] (len: 5, cap: 6)
s9(3): [2 3 4 1 2 3] (len: 6, cap: 6)
s9(4): [2 3 4 1 2 3 4] (len: 7, cap: 12)
s9(5): [2 3 4 1 2 3 4 5] (len: 8, cap: 12)
a1: [1 2 3 4 1 2 3] (len: 7, cap: 7)

可以看到对切片 s9 进行增加元素扩容后原始数组 a1 也发生了变化。

func main() {
	// 底层数组
	s2 := [4]int{10, 20, 30, 40}

	s3 := s2[:3]
	s4 := s2[2:]

	fmt.Printf("s3:%d\n", s3)
	fmt.Printf("s4:%d\n", s4)
	fmt.Println()

	// 修改其中一个切片
	s4[0] = 60
	fmt.Printf("s3:%d\n", s3)
	fmt.Printf("s4:%d\n", s4)
}

输出结果:

s3:[10 20 30]
s4:[30 40]

s3:[10 20 60]
s4:[60 40]