最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文總結(jié)Go語言切片核心知識點和坑

 更新時間:2023年06月05日 09:03:00   作者:半夏之沫  
都說Go的切片用起來絲滑得很,Java中的List怎么用,切片就怎么用,作為曾經(jīng)的Java選手,因為切片的使用不得當(dāng),喜提缺陷若干,本文就給大家總結(jié)一下Go語言切片核心知識點和坑,需要的朋友可以參考下

一. 切片結(jié)構(gòu)說明

切片(slice),是Go語言中對可變數(shù)組的抽象,相較于數(shù)組,具有可動態(tài)追加元素,可動態(tài)擴(kuò)容等更加實用的特性。切片在Go語言中對應(yīng)的結(jié)構(gòu)體源碼如下。

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

字段含義說明如下。

  • array,指向數(shù)組內(nèi)存地址的指針。切片底層存儲數(shù)據(jù)的結(jié)構(gòu)就是數(shù)組,切片使用一個指向數(shù)組的指針來操作這個數(shù)組;
  • len,切片的長度。len表示切片中可用的元素個數(shù),所謂可用,就是內(nèi)存空間被分配,并且通過當(dāng)前切片能夠訪問;
  • cap,切片的容量。cap表示切片最大的元素個數(shù),通常cap大于等于len,如果cap大于len,表示當(dāng)前切片有部分元素不可用,而不可用的意思就是內(nèi)存空間被分配,但是當(dāng)前切片無法訪問,訪問這些不可用元素會導(dǎo)致panic。

一個切片的示意圖如下。

二. 切片創(chuàng)建

切片的創(chuàng)建有多種方式,本節(jié)結(jié)合示例和圖示,對切片的不同創(chuàng)建方式進(jìn)行說明。

1. 直接創(chuàng)建切片

示例代碼如下所示。

// 直接創(chuàng)建切片
func directlyCreate() {
    slice := []int{1, 2, 3, 4, 5, 6, 7}
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(slice), cap(slice), &slice, slice)
}

上述示例代碼運(yùn)行打印如下。

len=7, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014240

此時創(chuàng)建出來的切片對應(yīng)圖示如下。

直接創(chuàng)建切片時,會為切片的底層數(shù)組開辟內(nèi)存空間并使用指定的元素對數(shù)組完成初始化,且創(chuàng)建出來的切片的len等于cap等于初始化元素的個數(shù)。

2. 從整個數(shù)組切得到切片

切片,顧名思義,其實就是可以從已經(jīng)存在的數(shù)組上切一段作為切片,本小節(jié)演示直接切整個數(shù)組得到切片,示例代碼如下所示。

// 從整個數(shù)組切得到切片
func sliceFromWholeArray() {
    originArray := [7]int{0, 1, 2, 3, 4, 5, 6}
    slice := originArray[:]
    fmt.Printf("originArrayAddress=%p\n", &originArray)
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(slice), cap(slice), &slice, slice)
}

上述示例代碼運(yùn)行打印如下。

originArrayAddress=0xc000014240
len=7, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014240

此時創(chuàng)建出來的切片對應(yīng)圖示如下。

從整個數(shù)組切,實際就是切片直接使用了這個數(shù)組作為底層的數(shù)組。

3. 從前到后切數(shù)組得到切片

本小節(jié)演示從前到后切數(shù)組得到切片,示例代碼如下所示。

// 從前到后切數(shù)組得到切片
func sliceFromArrayFrontToBack() {
    originArray := [7]int{0, 1, 2, 3, 4, 5, 6}
    slice := originArray[:4]
    fmt.Printf("origin array address is: [%p]\n", &originArray)
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(slice), cap(slice), &slice, slice)
}

上述在切數(shù)組時,沒有指定數(shù)組的開始索引,表示從索引0開始切(inclusive),指定了數(shù)組的結(jié)束索引,表示切到結(jié)束索引的位置(exclusive),運(yùn)行示例代碼,打印如下。

originArrayAddress=0xc000014240
len=4, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014240

此時創(chuàng)建出來的切片對應(yīng)圖示如下。

從前到后切數(shù)組得到的切片,len等于切的范圍的長度,對應(yīng)示例中索引0(inclusive)到索引4(exclusive)的長度4,而cap等于切的開始位置(inclusive)到數(shù)組末尾(inclusive)的長度7。

4. 從數(shù)組中間切到最后得到切片

本小節(jié)演示從數(shù)組中間切到最后得到切片,示例代碼如下所示。

// 從數(shù)組中間切到最后得到切片
func sliceFromArrayMiddleToLast() {
    originArray := [7]int{0, 1, 2, 3, 4, 5, 6}
    slice := originArray[4:]
    fmt.Printf("originArrayAddress=%p\n", &originArray)
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(slice), cap(slice), &slice, slice)
}

上述在切數(shù)組時,指定了數(shù)組的開始索引,表示從索引4(inclusive)開始切,沒有指定數(shù)組的結(jié)束索引,表示切到數(shù)組的末尾(inclusive),運(yùn)行示例代碼,打印如下。

originArrayAddress=0xc000014240
len=3, cap=3, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014260

此時創(chuàng)建出來的切片對應(yīng)圖示如下。

從數(shù)組中間切到最后得到的切片,len等于cap等于切的范圍的長度,對應(yīng)示例中索引4(inclusive)到數(shù)組末尾(inclusive)的長度3。并且由上述圖示可以看出,切片使用的底層數(shù)組其實還是被切的數(shù)組,只不過使用的是被切數(shù)組的一部分。

5. 從數(shù)組切一段得到切片

本小節(jié)演示從數(shù)組切一段得到切片,示例代碼如下所示。

// 從數(shù)組切一段得到切片
func sliceFromSelectionOfArray() {
    originArray := [7]int{0, 1, 2, 3, 4, 5, 6}
    slice := originArray[2:4]
    fmt.Printf("originArrayAddress=%p\n", &originArray)
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(slice), cap(slice), &slice, slice)
}

上述在切數(shù)組時,指定了數(shù)組的開始索引,表示從索引2(inclusive)開始切,也指定了數(shù)組的結(jié)束索引,表示切到數(shù)組的索引4的位置(exclusive),運(yùn)行示例代碼,打印如下。

originArrayAddress=0xc000014240
len=2, cap=5, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014250

此時創(chuàng)建出來的切片對應(yīng)圖示如下。

從數(shù)組切一段得到的切片,len等于切的范圍的長度,對應(yīng)示例中索引2(inclusive)到索引4(exclusive)的長度2,cap等于切的開始位置(inclusive)到數(shù)組末尾(inclusive)的長度5。并且,切片使用的底層數(shù)組還是被切數(shù)組的一部分。

6. 從切片切得到切片

除了切數(shù)組得到切片,還能切切片來得到切片,示例代碼如下所示。

// 從切片切得到切片
func sliceFromSlice() {
    originArray := [7]int{0, 1, 2, 3, 4, 5, 6}
    originSlice := originArray[:]
    derivedSlice := originSlice[2:4]
    fmt.Printf("originArrayAddress=%p\n", &originArray)
    fmt.Printf("originSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(originSlice), cap(originSlice), &originSlice, originSlice)
    fmt.Printf("derivedSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(derivedSlice), cap(derivedSlice), &derivedSlice, derivedSlice)
}

上述示例代碼中,originSlice是切數(shù)組originArray得到的切片,derivedSlice是切切片originSlice得到的切片,運(yùn)行示例代碼,打印如下。

originArrayAddress=0xc000014240
originSlice: len=7, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014240
derivedSlice: len=2, cap=5, sliceAddress=0xc000004090, sliceArrayAddress=0xc000014250

此時創(chuàng)建出來的切片對應(yīng)圖示如下。

從切片切得到切片后,兩個切片會使用同一個底層數(shù)組,區(qū)別就是可能使用的是底層數(shù)組的不同區(qū)域,因此如果其中一個切片更改了數(shù)據(jù),而這個數(shù)據(jù)恰好另一個切片可用訪問,那么另一個切片訪問該數(shù)據(jù)時就會發(fā)現(xiàn)數(shù)據(jù)發(fā)生了更改。但是請注意,雖然兩個切片使用同一個底層數(shù)組,但是切片的lencap都是獨(dú)立的,也就是假如其中一個切片通過類似于append() 函數(shù)導(dǎo)致len或者cap發(fā)生了更改,此時另一個切片的len或者cap是不會受影響的。

7. 使用make函數(shù)得到切片

make() 函數(shù)專門用于為slice,mapchan這三種引用類型分配內(nèi)存并完成初始化,make() 函數(shù)返回的就是引用類型對應(yīng)的底層結(jié)構(gòu)體本身,使用make() 函數(shù)創(chuàng)建slice的示例代碼如下所示。

// 使用make函數(shù)得到切片
func initializeByMake() {
    slice := make([]int, 5, 7)
    fmt.Printf("len=%d, cap=%d, slice=%v\n",
            len(slice), cap(slice), slice)
}

上述示例代碼中,會使用make() 函數(shù)創(chuàng)建一個int類型的切片,并指定len為5(第二個參數(shù)指定),cap為7(第三個參數(shù)指定),其中可以不指定cap,此時cap會取值為len。運(yùn)行示例代碼,打印如下。

len=5, cap=7, slice=[0 0 0 0 0]

此時訪問索引5或索引6的元素,會引發(fā)panic,示例代碼修改如下。

// 使用make函數(shù)得到切片
func initializeByMake() {
    slice := make([]int, 5, 7)
    fmt.Printf("len=%d, cap=%d, slice=%v\n",
            len(slice), cap(slice), slice)
    // 訪問索引5或索引6會引發(fā)panic
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()
    fmt.Println(slice[5])
}

運(yùn)行示例代碼,打印如下。

len=5, cap=7, slice=[0 0 0 0 0]
runtime error: index out of range [5] with length 5

那么索引5和索引6的元素怎么才能使用呢,就需要使用到內(nèi)建函數(shù)append(),示例代碼修改如下。

// 使用make函數(shù)得到切片
func initializeByMake() {
    slice := make([]int, 5, 7)
    fmt.Printf("len=%d, cap=%d, slice=%v\n",
            len(slice), cap(slice), slice)
    slice = append(slice, 1)
    slice = append(slice, 2)
    fmt.Printf("len=%d, cap=%d, slice=%v\n",
            len(slice), cap(slice), slice)
}

運(yùn)行示例代碼,打印如下。

len=5, cap=7, slice=[0 0 0 0 0]
len=7, cap=7, slice=[0 0 0 0 0 1 2]

append() 函數(shù)的使用,會在本文后面章節(jié)進(jìn)行說明。

三. 聲明切片占用內(nèi)存分析

使用如下方式聲明切片

var slice []int

然后使用如下方式打印僅聲明的切片占用的內(nèi)存大小。

fmt.Printf("memory=%d\n", unsafe.Sizeof(slice))

占用內(nèi)存大小打印如下。

memory=24

也就是僅聲明的切片會占用24字節(jié)的內(nèi)存大小,這個大小實際就是切片底層結(jié)構(gòu)體占用的大小,如下所示。

type slice struct {
    array unsafe.Pointer      // 64位操作系統(tǒng)下占8字節(jié)
    len   int		    // 64位操作系統(tǒng)下占8字節(jié)
    cap   int		    // 64位操作系統(tǒng)下占8字節(jié)
}

四. append函數(shù)

append() 函數(shù)用于將元素附加到切片的末尾,比如一個切片len為5,cap為7,此時可以使用append() 函數(shù)附加一個元素到切片的末尾使得切片的len變?yōu)?(cap此時不會改變),從而切片索引為5的位置的元素也可以讀和寫了。本節(jié)將對append() 函數(shù)的使用進(jìn)行說明。

1. 基本使用

append() 函數(shù)聲明如下所示。

func append(slice []Type, elems ...Type) []Type

append() 函數(shù)會將附加了元素后的切片返回,所以我們需要用切片變量來存儲append() 函數(shù)的返回值。

append() 函數(shù)的基本使用示例如下所示。

// 向切片附加元素
func appendItem() {
    slice := make([]int, 2, 7)
    fmt.Printf("len=%d, cap=%d, slice=%v\n",
            len(slice), cap(slice), slice)
    slice = append(slice, 10)
    fmt.Printf("len=%d, cap=%d, slice=%v\n",
            len(slice), cap(slice), slice)
}

運(yùn)行示例代碼,打印如下。

len=2, cap=7, slice=[0 0]
len=3, cap=7, slice=[0 0 10]

對應(yīng)圖示如下所示。

2. 觸發(fā)擴(kuò)容

已知切片有lencap,len表示可以讀和寫的元素個數(shù),cap表示當(dāng)前切片最大的元素個數(shù),那么cap減去len就是當(dāng)前切片還沒有使用的元素個數(shù),這部分元素需要通過append() 函數(shù)來附加到切片上。

當(dāng)附加元素到切片上后,會讓len加1,但是如果附加元素到切片之前len已經(jīng)等于cap,那么此時會先觸發(fā)擴(kuò)容再附加元素,擴(kuò)容的流程簡圖如下所示。

一旦觸發(fā)擴(kuò)容,會創(chuàng)建新容量大小的數(shù)組,然后將老數(shù)組的數(shù)據(jù)拷貝到新數(shù)組上,再然后將附加元素添加到新數(shù)組中,最后切片的array指向新數(shù)組。也就是說,切片擴(kuò)容會導(dǎo)致切片使用的底層數(shù)組地址發(fā)生變更,如下是示例代碼。

// 擴(kuò)容導(dǎo)致切片數(shù)組地址變更
func expansionCausedArrayAddressChange() {
    // 原始數(shù)組
    originArray := [7]int{0, 1, 2, 3, 4, 5, 6}
    // 原始切片
    originSlice := originArray[0:6]
    // 打印原始切片和原始數(shù)組的信息
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p, originArrayAddress=%p\n",
            len(originSlice), cap(originSlice), &originSlice, originSlice, &originArray)
    // 第一次append不會觸發(fā)擴(kuò)容
    firstAppendSlice := append(originSlice, 7)
    // 打印第一次Append后的切片和原始數(shù)組的信息
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p, originArrayAddress=%p\n",
            len(firstAppendSlice), cap(firstAppendSlice), &firstAppendSlice, firstAppendSlice, &originArray)
    // 第二次append會觸發(fā)擴(kuò)容
    secondAppendSlice := append(firstAppendSlice, 8)
    // 打印第二次Append后的切片和原始數(shù)組的信息
    fmt.Printf("len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p, originArrayAddress=%p\n",
            len(secondAppendSlice), cap(secondAppendSlice), &secondAppendSlice, secondAppendSlice, &originArray)
}

運(yùn)行示例代碼,打印如下。

len=6, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014240, originArrayAddress=0xc000014240
len=7, cap=7, sliceAddress=0xc0000040a8, sliceArrayAddress=0xc000014240, originArrayAddress=0xc000014240
len=8, cap=14, sliceAddress=0xc0000040d8, sliceArrayAddress=0xc0000100e0, originArrayAddress=0xc000014240

在示例代碼中,切數(shù)組originArray得到的切片如下所示。

第一次append元素后,切片如下所示。

第二次append元素時,會觸發(fā)擴(kuò)容,擴(kuò)容后的切片如下所示。

可見,擴(kuò)容后切片使用了另外一個數(shù)組作為了底層數(shù)組。

五. 切片注意事項

1. 問題演示

如下是一個踩坑的反面案例,代碼如下所示。

// 將slice作為參數(shù)傳遞到函數(shù)中
func appendTriggerExpansionCausingProblems() {
    outerSlice := make([]int, 6, 7)
    fmt.Printf("OuterSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(outerSlice), cap(outerSlice), &outerSlice, outerSlice)
    helpAppendNums(outerSlice)
    fmt.Printf("OuterSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(outerSlice), cap(outerSlice), &outerSlice, outerSlice)
}
func helpAppendNums(innerSlice []int) {
    for i := 0; i < 3; i++ {
        innerSlice = append(innerSlice, i)
    }
    fmt.Printf("InnerSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(innerSlice), cap(innerSlice), &innerSlice, innerSlice)
}

運(yùn)行示例代碼,打印如下。

OuterSlice: len=6, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014240
InnerSlice: len=9, cap=14, sliceAddress=0xc0000040a8, sliceArrayAddress=0xc0000100e0
OuterSlice: len=6, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014240

反面案例的代碼實現(xiàn)中,把一個len為6,cap為7的切片outterSlice作為參數(shù)傳遞到了一個helpAppendNums() 函數(shù)中,并在這個函數(shù)中進(jìn)行了3次append元素的操作,然后發(fā)現(xiàn)outterSlicelen,cap和底層數(shù)組的地址都沒有發(fā)生改變。

下面將結(jié)合圖示,對反面案例代碼整個流程進(jìn)行說明。

outerSlice創(chuàng)建出來時,outerSlice結(jié)構(gòu)如下所示。

因為Go語言中都是值傳遞,且切片變量本質(zhì)就是一個結(jié)構(gòu)體,所以把outerSlice作為實參調(diào)用helpAppendNums() 函數(shù)時,會把outerSlice值拷貝給到helpAppendNums() 函數(shù)的形參innerSlice,此時兩個切片的結(jié)構(gòu)如下所示。

這里其實就是一個容易踩坑特別是Go語言新手()容易踩坑的地方,因為在學(xué)習(xí)Go中的數(shù)據(jù)類型時,各種資料總是會告訴我在Go語言中有值類型引用類型的區(qū)分,而引用類型就是slice切片,map字典,interface接口,func函數(shù)和chan管道,此時如果這個Go語言新手()還會一點Java,那么就會認(rèn)為Go里面的sliceJava中的List一樣,畢竟都是引用嘛,那么把一個引用類型的slice傳遞到一個函數(shù)中,當(dāng)然在函數(shù)中操作的slice就應(yīng)該是傳遞的這個slice嘛。

上面用刪除線劃掉的部分,如果你真的有這樣的認(rèn)識,要么就是對Go的引用類型理解得有偏差(),要么就是對Java中聲明一個指向?qū)ο蟮淖兞繒r的內(nèi)存結(jié)構(gòu)沒有清晰的認(rèn)識(),或者都是()。先說Java中聲明一個指向?qū)ο蟮淖兞?,就像下面這樣。

List list = new ArrayList();

那么按照我們的正常認(rèn)知,上面的list是一個引用對吧,但是這里的list實際是一個指向堆上一個ArrayList對象的指針,存在于線程棧幀的局部變量表里,我們無論如何傳遞list變量(Java中也是值傳遞),實際都是傳遞的指向堆上ArrayList對象的指針,最終操作的都是堆上的ArrayList對象。

回到Go中的切片,我們聲明并創(chuàng)建一個切片,就像下面這樣。

slice := []int{1, 2, 3, 4, 5, 6, 7}

上面的slice字段就是一個切片結(jié)構(gòu)體,那么把這個slice字段傳遞到函數(shù)中時,是會直接拷貝這個切片給到函數(shù)的形參,此時實參和形參實際是兩個不同的切片,在內(nèi)存中有自己的空間和地址,只不過底層使用的是同一個數(shù)組而已,這里的拷貝,也稱作淺拷貝。

上面的坑踩完后,再回到反面案例代碼中,在helpAppendNums() 函數(shù)中會執(zhí)行3次append元素的操作,在執(zhí)行完第一次append操作后,outerSliceinnerSlice的結(jié)構(gòu)如下所示。

執(zhí)行完第一次append操作后,對于innerSlice來說len已經(jīng)等于cap了,又因為append的操作目標(biāo)是innerSlice,所以盡管底層數(shù)組數(shù)據(jù)發(fā)生了變更,但outerSlicelen是沒有發(fā)生變動的,并且也無法訪問索引為6的元素。

執(zhí)行完第二次append操作后,outerSliceinnerSlice的結(jié)構(gòu)如下所示。

因為第一次append后,innerSlicelen已經(jīng)和cap相等,所以第二次append時,innerSlice使用的底層數(shù)組是一個新的且容量翻倍的數(shù)組,那么從這時起,outerSliceinnerSlice使用的底層數(shù)組也不同了。

執(zhí)行完第三次append操作后,outerSliceinnerSlice的結(jié)構(gòu)如下所示。

2. 解決方式

既然把切片直接傳到函數(shù)中存在一些坑,那么相應(yīng)的就需要一些手段來解決。

第一種解決方式就是不直接傳遞切片,而是傳遞切片的指針,改進(jìn)代碼如下所示。

// 將slice作為參數(shù)傳遞到函數(shù)中
func appendTriggerExpansionCausingProblems() {
    outerSlice := make([]int, 6, 7)
    fmt.Printf("OuterSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(outerSlice), cap(outerSlice), &outerSlice, outerSlice)
    helpAppendNumsUsePointer(&outerSlice)
    fmt.Printf("OuterSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(outerSlice), cap(outerSlice), &outerSlice, outerSlice)
}
func helpAppendNumsUsePointer(innerSlice *[]int) {
    for i := 0; i < 3; i++ {
        *innerSlice = append(*innerSlice, i)
    }
    fmt.Printf("InnerSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(*innerSlice), cap(*innerSlice), innerSlice, *innerSlice)
}

運(yùn)行改進(jìn)代碼,打印如下。

OuterSlice: len=6, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014200
InnerSlice: len=9, cap=14, sliceAddress=0xc000004078, sliceArrayAddress=0xc0000100e0
OuterSlice: len=9, cap=14, sliceAddress=0xc000004078, sliceArrayAddress=0xc0000100e0

這種方式的好處從內(nèi)存的角度來說,僅進(jìn)行了一次指針的值傳遞,對內(nèi)存更友好。

第二種解決方式就是在函數(shù)中將處理后的切片返回,改進(jìn)代碼如下所示。

// 將slice作為參數(shù)傳遞到函數(shù)中
func appendTriggerExpansionCausingProblems() {
    outerSlice := make([]int, 6, 7)
    fmt.Printf("OuterSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(outerSlice), cap(outerSlice), &outerSlice, outerSlice)
    outerSlice = helpAppendNumsAndReturn(outerSlice)
    fmt.Printf("OuterSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(outerSlice), cap(outerSlice), &outerSlice, outerSlice)
}
func helpAppendNumsAndReturn(innerSlice []int) []int {
    for i := 0; i < 3; i++ {
        innerSlice = append(innerSlice, i)
    }
    fmt.Printf("InnerSlice: len=%d, cap=%d, sliceAddress=%p, sliceArrayAddress=%p\n",
            len(innerSlice), cap(innerSlice), &innerSlice, innerSlice)
    return innerSlice
}

運(yùn)行改進(jìn)代碼,打印如下。

OuterSlice: len=6, cap=7, sliceAddress=0xc000004078, sliceArrayAddress=0xc000014200
InnerSlice: len=9, cap=14, sliceAddress=0xc0000040a8, sliceArrayAddress=0xc0000100e0
OuterSlice: len=9, cap=14, sliceAddress=0xc000004078, sliceArrayAddress=0xc0000100e0

相較于第一種方式,由指針的值傳遞變更為了結(jié)構(gòu)體的值傳遞,內(nèi)存相對不友好。

總結(jié)

一圖流。

后記

切片的結(jié)構(gòu)簡潔明了,名字形象生動,使用靈活方便,完美符合Go語言的設(shè)計原則,但是為啥在我手里就用出了這么多簍子呢。

我一再反思,發(fā)現(xiàn)根因還是對于Go語言中的引用類型不夠理解,以及缺失對切片的底層源碼實現(xiàn)的了解,所以本文只能從現(xiàn)象入手討論切片的一些淺層次的使用,后續(xù)還是要深入源碼,結(jié)合Go語言的內(nèi)存模型詳細(xì)學(xué)習(xí)切片。

以上就是一文總結(jié)Go語言切片核心知識點和坑的詳細(xì)內(nèi)容,更多關(guān)于Go語言切片知識點和坑的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go并發(fā)限制之限流與控制并發(fā)數(shù)的實踐指南

    Go并發(fā)限制之限流與控制并發(fā)數(shù)的實踐指南

    在 Go 并發(fā)編程中,一個非常現(xiàn)實的問題是:goroutine很輕量,但不是無限資源,當(dāng)你隨手 go func() 起上成千上萬的協(xié)程時,系統(tǒng)可能不會立刻崩,但會在某個瞬間出現(xiàn)問題,所以一個核心能力就是:如何控制并發(fā)數(shù)量與訪問速率(限流),本文小編為大家詳細(xì)介紹一下
    2026-04-04
  • golang獲取當(dāng)前時間、時間戳和時間字符串及它們之間的相互轉(zhuǎn)換方法

    golang獲取當(dāng)前時間、時間戳和時間字符串及它們之間的相互轉(zhuǎn)換方法

    這篇文章主要介紹了golang獲取當(dāng)前時間、時間戳和時間字符串及它們之間的相互轉(zhuǎn)換,本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-04-04
  • GO語言中創(chuàng)建切片的三種實現(xiàn)方式

    GO語言中創(chuàng)建切片的三種實現(xiàn)方式

    這篇文章主要介紹了GO語言中創(chuàng)建切片的三種實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • golang組件swagger生成接口文檔實踐示例

    golang組件swagger生成接口文檔實踐示例

    這篇文章主要為大家介紹了golang組件swagger生成接口文檔實踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • golang實現(xiàn)對docker容器心跳監(jiān)控功能

    golang實現(xiàn)對docker容器心跳監(jiān)控功能

    這篇文章主要介紹了golang實現(xiàn)對docker容器心跳監(jiān)控功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • 在VS?Code中配置Go開發(fā)環(huán)境的完整過程

    在VS?Code中配置Go開發(fā)環(huán)境的完整過程

    如果您希望在本地計算機(jī)上開始使用Go語言進(jìn)行開發(fā),但尚未配置好VSCode中的Go支持,則可能是由于缺少必要工具鏈、擴(kuò)展或環(huán)境變量設(shè)置,可以看看這篇文章,這篇文章主要介紹了在VS Code中配置Go開發(fā)環(huán)境的完整過程,需要的朋友可以參考下
    2026-04-04
  • Go語言結(jié)合grpc和protobuf實現(xiàn)去中心化的聊天室

    Go語言結(jié)合grpc和protobuf實現(xiàn)去中心化的聊天室

    這篇文章主要為大家詳細(xì)介紹了Go語言如何結(jié)合grpc和protobuf實現(xiàn)去中心化的聊天室,文中的示例代碼講解詳細(xì),有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Go設(shè)計模式之觀察者模式講解和代碼示例

    Go設(shè)計模式之觀察者模式講解和代碼示例

    觀察者是一種行為設(shè)計模式, 允許一個對象將其狀態(tài)的改變通知其他對象,觀察者模式提供了一種作用于任何實現(xiàn)了訂閱者接口的對象的機(jī)制, 可對其事件進(jìn)行訂閱和取消訂閱,本文就通過代碼示例給大家詳細(xì)介紹一下Go的觀察者模式,需要的朋友可以參考下
    2023-07-07
  • Go語言面試題之select和channel的用法

    Go語言面試題之select和channel的用法

    金九銀十面試季到了(PS:貌似今年一年都是面試季),就業(yè)環(huán)境很差,導(dǎo)致從業(yè)人員不得不卷。本文將重點講解一下Go面試進(jìn)階知識點之select和channel,需要的可以參考一下
    2022-09-09
  • Go語言設(shè)置JSON的默認(rèn)值操作

    Go語言設(shè)置JSON的默認(rèn)值操作

    這篇文章主要介紹了Go語言設(shè)置JSON的默認(rèn)值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評論

宾阳县| 林西县| 弋阳县| 旺苍县| 阿克苏市| 定远县| 嘉定区| 布尔津县| 喀喇沁旗| 太谷县| 苗栗县| 景谷| 资阳市| 通城县| 怀柔区| 佛学| 洱源县| 梧州市| 兰坪| 平远县| 正蓝旗| 卫辉市| 霍城县| 旌德县| 瑞丽市| 共和县| 桐乡市| 汉寿县| 噶尔县| 慈溪市| 滨州市| 迭部县| 大城县| 政和县| 郯城县| 北碚区| 呼伦贝尔市| 铁力市| 华安县| 资溪县| 青铜峡市|