天天看點

golang 定義一個空切片_深入了解 golang 切片機制(理)

切片總是要有一個交代的,來看看這段代碼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