golang之slice切片原始碼解析

2021-10-17 23:36:12 字數 3525 閱讀 5386

切片作為常用的資料結構體之一,切片實際上是陣列的抽象,也稱動態陣列,顧名思義,它自帶擴容的機制,因為其靈活性,相對陣列來說被運用的更加廣泛。

golang的切片實現是在包runtime/slice.go,切片結構體包含array指向陣列的指標,是一塊連續的記憶體空間,len代表切片的長度,cap代表切片的容量,cap總是大於等於len。

從上圖可以看出,切片是在陣列的基礎上抽象了一層,底層是對陣列的引用,當切片發生擴容時,底層陣列發生改變,而對於上層切片來說是沒有變化的。

先來看看slice的初始化,slice的初始化可以通過make關鍵字,傳入type、len、cap。

make(type,len,cap)

var numbers =

make([

]int64,5,6)

slice的make初始化主要通過runtime.makeslice來完成,先計算出需要的記憶體空間大小,然後再分配記憶體。

func

makeslice

(et *_type,

len,

capint

) unsafe.pointer

panicmakeslicecap()

}//分配記憶體

return

mallocgc

(mem, et,

true

)}

記憶體空間大小的計算公式為:

記憶體空間大小 = 切片中元素大小 * 容量大小

func

growslice

(et *_type, old slice,

capint

) slice

else

else

// set newcap to the requested cap when

// the newcap calculation overflowed.

if newcap <=0}

}}....

.

在一開始舊切片old.cap可能還未初始化,old.cap為0,這時候new.cap直接等於預期的容量cap

當舊切片的長度old.len < 1024時,進行兩倍擴容new.cap= 2(old.cap)

當舊切片的長度old.len > 1024時,進行1.25倍擴容new.cap = 1.25(old.cap)

我們知道,slice的空間大小等於元素size * cap,當舊切片的長度大於1024時,記憶體大小已經達到乙個量級,如果還繼續2倍擴容,那麼消耗的記憶體空間將是非常大的。

接下來,在計算出新的容量的情況下,就需要準備去申請足夠空間的記憶體,但之前還需要一系列記憶體對齊的計算操作:

當陣列中元素所佔的位元組大小為1、8或者2的倍數時,對應相應的記憶體空間計算。

func

growslice

(et *_type, old slice,

capint

) slice

else

//計算記憶體空間轉化為用位運算

lenmem =

uintptr

(old.

len)

<< shift

newlenmem =

uintptr

(cap

)<< shift

capmem =

roundupsize

(uintptr

(newcap)

<< shift)

overflow =

uintptr

(newcap)

>

(maxalloc >> shift)

newcap =

int(capmem >> shift)

default

: lenmem =

uintptr

(old.

len)

* et.size

newlenmem =

uintptr

(cap

)* et.size

capmem, overflow = math.

muluintptr

(et.size,

uintptr

(newcap)

) capmem =

roundupsize

(capmem)

newcap =

int(capmem / et.size)

}}

計算出需要分配的記憶體大小後,就會重新申請記憶體,然後將原來切片的元素重新賦值到新的切片中。

func

growslice

(et *_type,old slice,

capint

)slice

else

}//將舊切片的值拷入新的切片

memmove

(p, old.array, lenmem)

}

拷貝切片可以用copy方法

func copy(dst, src type) int

func

main()

var number2 =

make([

]int64

,len

(number1)

)copy

(number2,number1)

fmt.

println

(number2)

}

實際上copy根據資料型別,最終會呼叫切片的runtime.slicecopy方法。

func

slicecopy

(toptr unsafe.pointer, tolen int

, fmptr unsafe.pointer, fmlen int

, width uintptr

)int

//根據源切片和目標切片的長度,以長度最小的切片進行拷貝

n := fmlen

if tolen < n

if width ==

0//拷貝的空間大小=長度 * 元素大小

size :=

uintptr

(n)* width

if size ==

1else

return n

}

從原始碼可以看出在切片拷貝的時候,要預先定義切片的長度再進行拷貝,否則有可能拷貝失敗。

在擴容過程中,切片的位址不會被改變,改變的是切片的底層陣列array,會申請一塊新的記憶體位址替換。

slice沒有縮小容量的操作

golang筆記 Slice切片

sliceslice代表變長的序列,序列中每個元素都有相同的型別。語法和陣列很像,只是沒有固定長度。引用型別 動態陣列 從概念上來說,slice像乙個結構體,這個結構體包含了三個元素 乙個指標,指向陣列中slice指定的開始位置 長度,即slice的長度 最大長度,也就是slice開始位置到陣列的最...

golang學習(九) 切片(slice)

在go 語言中,陣列是值型別,長度是固定的 而切片是引用型別,長度可動態增長。切片的內部結構包括位址 大小和容量。特性 1.當省略開始位置時,表示從連續區域開頭到結束位置。2.當省略結束位置時,表示從開始位置到整個連續區域結束位置。3.兩者都省略時,與切片本身等效。基本格式如下 slice 開始位置...

Golang 切片slice簡要歸納

切片的本質是乙個連續記憶體的陣列。切片由以下三個部分組成 成員空間 當前成員數 最大成員數 當前成員數即為len,最大成員數即capacity 這個屬性和字串末尾的位元組個數類似 使用len的原因是為了防止切片成員發生越界。切片是長度可變的,因此在成員數增加的過程中會發生擴容,具體的判斷規則如下 原...