切片作為常用的資料結構體之一,切片實際上是陣列的抽象,也稱動態陣列,顧名思義,它自帶擴容的機制,因為其靈活性,相對陣列來說被運用的更加廣泛。
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的原因是為了防止切片成員發生越界。切片是長度可變的,因此在成員數增加的過程中會發生擴容,具體的判斷規則如下 原...