理解Go中的slice

2022-07-28 03:15:09 字數 2365 閱讀 8184

最近閒來無事,深入研究了slice在golang中的實現並簡要閱讀了其相關的底層實現**後,對於實際工作中的一些slice相關**的寫法與bug有了一種豁然開朗的感覺。故記錄下來,與君分享。

陣列 vs 切片

對於初學者來說,我們必須分清楚陣列與切片的區別。

在go中,陣列與其他語言並無太大區別,都是一段指定長度的連續記憶體空間。例如如下**,我們宣告乙個長度為4,型別為int的陣列a

var a [4]int
此時,go會呼叫mallocgc函式為我們申請一段連續的記憶體空間,如下圖所示。

而對於slice,其實際上是一種結構體,基於陣列的一種抽象,相當於其他語言中的動態陣列。

切片的表示

runtime包中,切片的定義如下。

type slice struct
array屬性指向乙個底層陣列,實際資料儲存在其中,對於同乙個底層陣列,其可以被多個切片引用。

len屬性限制了切片的長度,通過arraylen屬性,我們就能劃分出陣列上的一段資料。

cap屬性表示切片的容量,其以array所指向的位址為起始位置,陣列末尾為結束位置。兩者之間的跨度即為cap的大小。

舉例說明,這裡我們建立乙個切片。

s1 := make(byte, 4, 6)

s1[0] = 'a'

s1[1] = 'b'

s1[2] = 'c'

s1[3] = 'd'

其在記憶體中表示如下所示。

此時,我們再建立乙個切片s2 := s1[1:3]。其表示如下所示。

由於s2是基於s1傳建的,因此其指向了同一底層陣列,不同的是,s2的array指向的是b元素的起始記憶體位址。顯而易見,當我們執行s1[1] = 'x'語句時,s2[0]的值也會被改變為x

空切片 vs nil切片

nil切片var s byte,如下圖所示。

空切片s := make(byte, 0),如下圖所示。

nil切片array屬性為nil,而空切片array屬性為空陣列的位址。

切片的擴容

首先,要明確,擴容針對的是切片的容量而非底層陣列的實際長度!

顯然,s1[4]的值也會因此而被改變為e

切片**編寫建議

在實際工作中,針對切片操作,我們應該注意哪些呢?

及時釋放大切片。例如下述**

for i := 0; i < 3; i++
雖然我們只使用了tmp的前10個位元組,但是其指向的底層陣列依然是原來tmp指向的大陣列,因此gc不會**該大陣列,從而導致記憶體占用過大。為了避免此問題,我們可以建立乙個小切片,然後將前10個位元組複製過去,如下**所示。

for i := 0; i < 3; i++
使用for range語句時,例如

for i, name := range names
其中name變數是值拷貝,每一次迭代都將對應i位置上的值拷貝到變數name上,因此你無法通過改變name的值來改變切片names對應位置上的值。當然,我們都知道在迭代中修改原切片是十分不好的程式設計習慣。

參考

go語言 slice 迭代slice

go語言內建乙個關鍵字range用於迭代集合,當然他也可以迭代slice,也可以使用 來忽略我們不關心的元素,但是如果只關心index則不需這麼寫 for index,range slice1。下在給出完整 package main import fmt func main for index,va...

Go語言切片 Slice

python裡面切片是一種操作,用於取list裡面元素。而go語言的切片則是一種資料型別,而不是一種操作。go語言中提供了切片 slice 作為一種更為靈活 功能強悍的內建型別,它其實是陣列的一種抽象。切片的原始碼 type slice struct slice是原陣列在記憶體中的位址的乙個指標,它...

go 陣列與slice的區別

總結 2.相同大小陣列可以賦值,會拷貝全部內容。slice賦值和指標一樣。陣列和slice之間不能相互賦值。當然slice有自己的copy函式 3.陣列也可以進行切片,返回值是乙個slice,改變slice時會同步修改陣列內容,相當於取得了這個陣列的指標 測試 陣列定義方式 1 寫明長度 c1 5 ...