golang入门slice切片的原理以及内置函数cap, len
golang中slice(切片)是常用的类型, slice是对数组进行封装
package main
import (
"fmt"
"strconv"
)
func testLenCap() {
strs := make([]string, 5, 10) //可以只有一个数字参数, 那么cap=len, make([]string, 5, 5) 等价于 make([]string, 5)
fmt.Printf("value=%v, is nil=%v\n", strs, strs == nil)
//strs := []string{"0", "1", "2", "3", "4"} //当然这种方式是直接赋值了, 忽略赋值等价于 make([]string, 5)
for i := 0; i < len(strs); i++ {
strs[i] = strconv.Itoa(i)
}
fmt.Printf("len=%v, cap=%v, strsAddress=%p, valueAddress=%p\n", len(strs), cap(strs), &strs, strs) //%p内存地址
for i := len(strs); i < cap(strs); i++ {
//strs[i] = strconv.Itoa(i) //panic
strs = append(strs, strconv.Itoa(i)) //使用append向数组追加数据
}
fmt.Printf("len=%v, cap=%v, strsAddress=%p, valueAddress=%p\n", len(strs), cap(strs), &strs, strs) //内存地址不变
strs = append(strs, "10")
fmt.Printf("len=%v, cap=%v, strsAddress=%p, valueAddress=%p\n", len(strs), cap(strs), &strs, strs) //内存地址改变
//strs2自身的地址与strs不一样, 但实际数组的内存地址不变, 改变strs, strs2其中任意一个变量, 另一个随之改变
strs2 := strs[:]
fmt.Printf("len=%v, cap=%v, strsAddress=%p, valueAddress=%p\n", len(strs2), cap(strs2), &strs2, strs2)
strs2[5] = "1000"
fmt.Printf("strs[5]=%s\n", strs[5])
//strs3自身的地址与strs不一样, 实际数组的内存地址也不一样, 互不干扰.
startIndex := 5
strs3 := strs[startIndex:7]
fmt.Printf("len=%v, cap=%v, strsAddress=%p, valueAddress=%p\n", len(strs3), cap(strs3), &strs3, strs3)
fmt.Printf("cap(strs3) == cap(strs)-startIndex : %v", cap(strs3) == cap(strs)-startIndex)
}
func main() {
testLenCap()
}
控制台打印结果:
value=[ ], is nil=false
len=5, cap=10, strsAddress=0xc0420023e0, valueAddress=0xc04203e0a0
len=10, cap=10, strsAddress=0xc0420023e0, valueAddress=0xc04203e0a0
len=11, cap=20, strsAddress=0xc0420023e0, valueAddress=0xc042040140
len=11, cap=20, strsAddress=0xc0420024a0, valueAddress=0xc042040140
strs[5]=1000
len=2, cap=15, strsAddress=0xc0420024e0, valueAddress=0xc042040190
cap(strs3) == cap(strs)-startIndex : true
可以看到:
len函数是实际数据存长度;
cap是最大容量,可以避免反复分配内存;
使用原切片创建新切片时, 应注意每个切片的值, 避免出现与预想不一样的情况