- 切片总是要有一个交代的,来看看这段代码code
package main
import (
"fmt"
"unsafe"
)
type Slice struct {
ptr unsafe.Pointer // Array pointer
len int // slice length
cap int // slice capacity
}
// 因为需要指针计算,所以需要获取int的长度
// 32位 int length = 4
// 64位 int length = 8
var intLen = int(unsafe.Sizeof(int(0)))
func main() {
s := make([]int, 10, 20)
// 利用指针读取 slice memory 的数据
if intLen == 4 { // 32位
m := *(*[4 + 4*2]byte)(unsafe.Pointer(&s))
fmt.Println("slice memory:", m)
} else { // 64 位
m := *(*[8 + 8*2]byte)(unsafe.Pointer(&s))
fmt.Println("slice memory:", m)
}
// 把slice转换成自定义的 Slice struct
slice := (*Slice)(unsafe.Pointer(&s))
fmt.Println("slice struct:", slice)
fmt.Printf("ptr:%v len:%v cap:%v \n", slice.ptr, slice.len, slice.cap)
fmt.Printf("golang slice len:%v cap:%v \n", len(s), cap(s))
s[0] = 0
s[1] = 1
s[2] = 2
// 转成数组输出
arr := *(*[3]int)(unsafe.Pointer(slice.ptr))
fmt.Println("array values:", arr)
// 修改 slice 的 len
slice.len = 15
fmt.Println("Slice len: ", slice.len)
fmt.Println("golang slice len: ", len(s))
}
- 看出什么来了么?
- 储备知识:
- unsafe.Pointer 类似于c语言中的void*
- unsafe.Pointer所占空间 = int 类型所占字节数
- 结论:
- slice的结构等同于Slice结构体
- 储备知识:
// 每次cap改变,指向array的ptr就会变化一次
s := make([]int, 1)
fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))
for i := 0; i < 5; i++ {
s = append(s, i)
fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))
}
fmt.Println("Array:", s)
- 替换上面的主程序,你发现了什么呢?
- 每次append,地址都会发生变化 ,说明当cap不够的时候,会产生复制操作。
- 实际go在append的时候放大cap是有规律的。在 cap 小于1024的情况下是每次扩大到 2 * cap ,当大于1024之后就每次扩大到 1.25 * cap 。所以上面的测试中cap变化是 1, 2, 4, 8
有疑问加站长微信联系(非本文作者)