亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

切片(slice),是Go語言中對可變數(shù)組的抽象,相較于數(shù)組,具有可動態(tài)追加元素,可動態(tài)擴容等更加實用的特性。切片在Go語言中對應的結(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)存空間被分配,并且通過當前切片能夠訪問;
  • cap,切片的容量。cap表示切片最大的元素個數(shù),通常cap大于等于len,如果cap大于len,表示當前切片有部分元素不可用,而不可用的意思就是內(nèi)存空間被分配,但是當前切片無法訪問,訪問這些不可用元素會導致panic。

一個切片的示意圖如下。

二. 切片創(chuàng)建

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

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)
}

上述示例代碼運行打印如下。

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

此時創(chuà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)
}

上述示例代碼運行打印如下。

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

此時創(chuà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),運行示例代碼,打印如下。

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

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

從前到后切數(shù)組得到的切片,len等于切的范圍的長度,對應示例中索引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),運行示例代碼,打印如下。

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

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

從數(shù)組中間切到最后得到的切片,len等于cap等于切的范圍的長度,對應示例中索引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),運行示例代碼,打印如下。

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

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

從數(shù)組切一段得到的切片,len等于切的范圍的長度,對應示例中索引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得到的切片,運行示例代碼,打印如下。

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

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

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

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

make() 函數(shù)專門用于為slice,mapchan這三種引用類型分配內(nèi)存并完成初始化,make() 函數(shù)返回的就是引用類型對應的底層結(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。運行示例代碼,打印如下。

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])
}

運行示例代碼,打印如下。

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)
}

運行示例代碼,打印如下。

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é)進行說明。

三. 聲明切片占用內(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ù)的使用進行說明。

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)
}

運行示例代碼,打印如下。

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

對應圖示如下所示。

2. 觸發(fā)擴容

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

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

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

// 擴容導致切片數(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ā)擴容
    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ā)擴容
    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)
}

運行示例代碼,打印如下。

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ā)擴容,擴容后的切片如下所示。

可見,擴容后切片使用了另外一個數(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)
}

運行示例代碼,打印如下。

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ù)中進行了3次append元素的操作,然后發(fā)現(xiàn)outterSlicelen,cap和底層數(shù)組的地址都沒有發(fā)生改變。

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

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

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

List list = new ArrayList();

那么按照我們的正常認知,上面的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的操作目標是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ù)中存在一些坑,那么相應的就需要一些手段來解決。

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

// 將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)
}

運行改進代碼,打印如下。

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)存的角度來說,僅進行了一次指針的值傳遞,對內(nèi)存更友好。

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

// 將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
}

運行改進代碼,打印如下。

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語言的設計原則,但是為啥在我手里就用出了這么多簍子呢。

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

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

相關文章

  • Golang并發(fā)利器sync.Once的用法詳解

    Golang并發(fā)利器sync.Once的用法詳解

    在某些場景下,我們需要初始化一些資源。有時會采用延遲初始化的方式,在真正需要資源的時候才進行初始化。在這種情況下,Go語言中的sync.Once提供一個優(yōu)雅且并發(fā)安全的解決方案,本文將對其進行詳細介紹
    2023-04-04
  • Golang 經(jīng)典校驗庫 validator 用法解析

    Golang 經(jīng)典校驗庫 validator 用法解析

    這篇文章主要為大家介紹了Golang 經(jīng)典校驗庫 validator 用法解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Go構(gòu)建器模式構(gòu)建復雜對象方法實例

    Go構(gòu)建器模式構(gòu)建復雜對象方法實例

    本文介紹了構(gòu)建器模式,如何通過構(gòu)建器對象構(gòu)建復雜業(yè)務對象的方法實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • GoPath模式和GoMoudle模式的相愛相殺

    GoPath模式和GoMoudle模式的相愛相殺

    這篇文章主要介紹了GoPath模式和GoMoudle模式的相愛相殺,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • go語言中int和byte轉(zhuǎn)換方式

    go語言中int和byte轉(zhuǎn)換方式

    這篇文章主要介紹了go語言中int和byte轉(zhuǎn)換方式,需要的朋友可以參考下
    2018-11-11
  • Go語言interface詳解

    Go語言interface詳解

    這篇文章主要介紹了Go語言interface詳解,本文講解了什么是interface、interface類型、interface值、空interface、interface函數(shù)參數(shù)等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • 總結(jié)Golang四種不同的參數(shù)配置方式

    總結(jié)Golang四種不同的參數(shù)配置方式

    這篇文章主要介紹了總結(jié)Golang四種不同的參數(shù)配置方式,文章圍繞主題展開詳細的內(nèi)容戒殺,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • golang中sync.Once只執(zhí)行一次的原理解析

    golang中sync.Once只執(zhí)行一次的原理解析

    在某些場景下,我們希望某個操作或者函數(shù)僅被執(zhí)行一次,比如單例模式的初始化,一些資源配置的加載等,golang中的sync.Once就實現(xiàn)了這個功能,本文就和大家一起解析sync.Once只執(zhí)行一次的原理,需要的朋友可以參考下
    2023-09-09
  • golang中的select關鍵字用法總結(jié)

    golang中的select關鍵字用法總結(jié)

    這篇文章主要介紹了golang中的select關鍵字用法總結(jié),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • 解決goland中編輯tpl文件不高亮沒智能補全的問題

    解決goland中編輯tpl文件不高亮沒智能補全的問題

    這篇文章主要介紹了解決goland中編輯tpl文件不高亮沒智能補全的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評論