Golang slice 切片原理

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

golang 中的 slice 非常強大,讓數組操作非常方便高效。在開發中不定長度表示的數組全部都是 slice 。但是很多同學對 slice 的模糊認識,造成認為golang中的數組是參考型別,結果就是在實際開發中碰到很多坑,以至於出現一些莫名奇妙的問題,數組中的資料丟失了。

下面我們就開始詳細理解下 slice ,理解後會對開發出高效的程式非常有協助。

這個是 slice 的資料結構,它很簡單,一個指向真實 array 地址的指標 ptrslice 的長度 len 和容量 cap


其中 lencap 就是我們在調用 len(slice)cap(slice) 返回的值。

我們來按照 slice 的資料結構定義來解析出 ptr, len, cap

// 按照定義的資料結構type Slice struct {    ptr   unsafe.Pointer        // Array pointer    len   int               // slice length    cap     int               // slice capacity}

下面寫一個完整的程式,嘗試把golang中slice的記憶體地區轉換成我們定義的 Slice 進行解析

package mainimport (    "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 = 8var 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))}

運行一下查看結果

$ go run slice.goslice memory: [0 64 6 32 200 0 0 0 10 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0]slice struct: &{0xc820064000 10 20}ptr:0xc820064000 len:10 cap:20golang slice len:10 cap:20array values: [0 1 2]Slice len:  15golang slice len:  15

看到了,golang slice 的memory內容,和自訂的 Slice 的值,還有按照 slice 中的指標指向的記憶體,就是實際 Array 資料。當修改了 slice 中的len, len(s) 也變了。

接下來結合幾個例子,瞭解下slice一些用法

聲明一個Array通常使用 make ,可以傳入2個參數,也可傳入3個參數,第一個是資料類型,第二個是 len ,第三個是 cap 。如果不穿入第三個參數,則 cap=lenappend 可以用來向數組末尾追加資料。

這是一個 append 的測試

// 每次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)

運行結果

len:1 cap: 1 array ptr: 0xc8200640f0len:2 cap: 2 array ptr: 0xc820064110len:3 cap: 4 array ptr: 0xc8200680c0len:4 cap: 4 array ptr: 0xc8200680c0len:5 cap: 8 array ptr: 0xc82006c080len:6 cap: 8 array ptr: 0xc82006c080Array: [0 0 1 2 3 4]

看出來了吧,每次cap改變的時候指向array記憶體的指標都在變化。當在使用 append 的時候,如果 cap==len 了這個時候就會新開闢一塊更大記憶體,然後把之前的資料複製過去。

實際go在append的時候放大cap是有規律的。在 cap 小於1024的情況下是每次擴大到 2 * cap ,當大於1024之後就每次擴大到 1.25 * cap 。所以上面的測試中cap變化是 1, 2, 4, 8

在實際使用中,我們最好事先預期好一個cap,這樣在使用append的時候可以避免反覆重新分配記憶體複製之前的資料,減少不必要的效能消耗。

建立切片

s := []int{1, 2, 3, 4, 5}fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))fmt.Println("Array:", s)s1 := s[1:3]fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s1), cap(s1), *(*unsafe.Pointer)(unsafe.Pointer(&s1)))fmt.Println("Array", s1)

運行結果

len:5 cap: 5 array ptr: 0xc820012210Array: [1 2 3 4 5]len:2 cap: 4 array ptr: 0xc820012218Array [2 3]

在一個切片基礎上建立新的切片 s1 ,新切片的 ptr 指向的就是 s1[0] 資料的記憶體位址。可以看到指標地址 0xc8200122100xc820012218 相差 8byte 正好是一個int類型長度,cap也相應的變為4

就寫到這裡了,總結一下,切片的結構是指向資料的指標,長度和容量。複製切片,或者在切片上建立新切片,切片中的指標都指向相同的資料記憶體地區。

知道了切片原理就可以在開發中避免出現錯誤了,希望這篇部落格可以給大家帶來協助。

參考:https://blog.golang.org/go-slices-usage-and-internals

附上 go 源碼中 slice 的資料結構定義

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.