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

Go語言Slice切片底層的實現(xiàn)

 更新時間:2025年04月15日 09:25:43   作者:明天不下雨(牛客同名)  
本文主要介紹了Go語言Slice切片底層的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

Go語言(Golang)中切片(slice)的相關(guān)知識、包括切片與數(shù)組的關(guān)系、底層結(jié)構(gòu)、擴容機制、以及切片在函數(shù)傳遞、截取、增刪元素、拷貝等操作中的特性。并給出了相關(guān)代碼示例和一道面試題。關(guān)鍵要點包括:

數(shù)組特性:Go語言中數(shù)組是一個值、數(shù)組變量表示整個數(shù)組、不同于C語言中指向第一個元素的指針。傳遞數(shù)組到函數(shù)或拷貝數(shù)組時、會有不同的內(nèi)存地址和數(shù)據(jù)獨立性表現(xiàn)。

切片定義:切片是建立在Go數(shù)組之上的抽象類型、其底層結(jié)構(gòu)包含指向底層數(shù)組的指針、長度和容量。

切片擴容

  • 新切片長度大于舊切片容量兩倍時、新容量為新長度
  • 舊容量小于256時、新容量為舊容量兩倍
  • 否則按1.25倍增速擴容、還會進行內(nèi)存對齊。

函數(shù)傳遞:切片通過函數(shù)傳遞時、傳的是切片結(jié)構(gòu)、在函數(shù)內(nèi)改變切片可能影響函數(shù)外的切片、取決于底層數(shù)組是否變化。

切片操作

  • 通過 “:” 作截取切片、新切片與原切片共享底層數(shù)組
  • 刪除元素可通過拼接切片實現(xiàn)
  • 新增元素使用append操作
  • 深度拷貝可使用copy函數(shù)。

1.切片是什么

在Go語言中 切片(slice)是建立在數(shù)組之上的一種抽象類型。切片提供了一種更靈活的方式來處理數(shù)組、它允許動態(tài)地改變數(shù)組的大小、并且可以方便地進行切片操作。理解切片之前、我們需要先了解數(shù)組。

Go的數(shù)組 在Go語言中、數(shù)組的長度是類型的一部分、這意味著數(shù)組的長度是固定的、不能改變。

數(shù)組的傳遞和拷貝行為與C語言不同、Go語言中的數(shù)組是值類型、傳遞數(shù)組時會進行值拷貝。

1.1 示例一:

將數(shù)組傳遞到函數(shù)中 數(shù)組的地址不一樣

package main

import "fmt"

func main() {
    array := [3]int{1, 2, 3}
    // 數(shù)組傳遞到函數(shù)中
    test(array)
    fmt.Printf("array 外: %p\n", &array)
}

func test(array [3]int) {
    fmt.Printf("array 內(nèi): %p\n", &array)
    }

由于數(shù)組是值類型、傳遞數(shù)組時會進行值拷貝、因此在test函數(shù)中打印的地址與main函數(shù)中打印的地址不同。

1.2 值拷貝

值拷貝意味著拷貝的是變量的內(nèi)容、而不是內(nèi)存地址。因此拷貝出來的變量有自己的獨立副本、內(nèi)容相同、但它們存儲在不同的內(nèi)存地址中。

Go 語言中的切片(slice)是動態(tài)擴容的。當(dāng)你向切片中添加元素時、Go 會自動管理切片的大小、并在需要時進行擴容。

具體行為:

  • 初始容量:當(dāng)你創(chuàng)建一個切片時、Go 會為切片分配一個初始容量。如果你添加的元素超過了切片當(dāng)前的容量Go 會自動擴容。
  • 擴容規(guī)則:Go 會根據(jù)當(dāng)前切片的容量自動擴展切片的大小、通常是原來容量的2倍。擴容后、切片的長度和容量都會增加。
  • 內(nèi)部機制:當(dāng)切片擴容時、Go 會為新切片分配新的底層數(shù)組、并將原數(shù)組的元素拷貝到新數(shù)組中。這是一個代價比較高的操作、尤其是在需要多次擴容的情況下

2.底層結(jié)構(gòu)

type slice struct {
    // 底層數(shù)組指針(或者說是指向一塊連續(xù)內(nèi)存空間的起點)
    array unsafe.Pointer
    // 長度
    len  int
    // 容量
    cap  int
}

在這個結(jié)構(gòu)中:

  • array:指向底層數(shù)組的指針、或者說是指向一塊連續(xù)內(nèi)存空間的起點。
  • len:切片的長度、即切片中實際包含的元素數(shù)量。
  • cap:切片的容量、即切片可以包含的元素的最大數(shù)量,不包括可能的擴展空間。

切片擴容

計算目標(biāo)容量

case1: 如果新切片的長度大于舊切片容量的兩倍、則新切片容量就為新切片的長度。

case2:

  • 如果舊切片的容量小于256、那么新切片的容量就是舊切片的容量的兩倍。
  • 反之需要用舊切片容量按照1.25倍的增速、直到大于新切片長度。

為了更平滑的過渡、每次擴大1.25倍、還會加上3/4 * 256

進行內(nèi)存對齊、需要按照Go內(nèi)存管理的級別去對齊內(nèi)存、最終容量以這個為準(zhǔn)。

3.切片問題

3.1 切片通過函數(shù)傳的是什么

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    s := make([]int, 5, 10)
    PrintSliceStruct(&s)
    test(s)
}

func test(s []int) {
    PrintSliceStruct(&s)
}

func PrintSliceStruct(s *[]int) {
    // 代碼 將slice 轉(zhuǎn)換成 reflect.SliceHeader
    ss := (*reflect.SliceHeader)(unsafe.Pointer(s))

    // 查看slice的結(jié)構(gòu)
    fmt.Printf("slice struct: %+v, slice is %v\n", ss, s)
}

控制臺輸出:

slice struct: &{Data:1374389649568 Len:5 Cap:10}, slice is &[0 0 0 0 0]
slice struct: &{Data:1374389649568 Len:5 Cap:10}, slice is &[0 0 0 0 0]

切片的定義:你創(chuàng)建了一個切片 s、通過 make([]int, 5, 10) 創(chuàng)建了一個長度為 5、容量為 10 的切片。

也就是說它初始化了一個包含 5 個元素且最大容量為 10 的底層數(shù)組

總結(jié):

切片傳遞:當(dāng)切片通過參數(shù)傳遞到函數(shù)時、傳遞的是切片的值、但切片內(nèi)部的底層數(shù)組地址(指針)并沒有被復(fù)制。

PrintSliceStruct 打印的結(jié)構(gòu):無論是在 main 函數(shù)還是 test 函數(shù)中、切片的底層數(shù)組地址、長度和容量都是相同的、因為底層數(shù)組是共享的。

為什么輸出相同:

輸出顯示的 Data 地址、Len 和 Cap 是一致的、因為 test(s) 傳遞的是切片的值(即切片的結(jié)構(gòu)),但切片中的指針指向相同的底層數(shù)組。所以無論是傳遞給 PrintSliceStruct 函數(shù)的 s、還是 test 函數(shù)中的 s、它們指向的是同一個底層數(shù)組、并且它們的長度和容量保持一致

3.2 在函數(shù)里面改變切片 函數(shù)外的切片會被影響嗎

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    s := make([]int, 5) // 創(chuàng)建一個長度為 5 的切片
    case1(s)             // 調(diào)用 case1 函數(shù)
    case2(s)             // 調(diào)用 case2 函數(shù)
    PrintSliceStruct(&s) // 打印切片結(jié)構(gòu)
}

// 底層數(shù)組不變
func case1(s []int) {
    s[1] = 1 // 修改切片中的元素
    PrintSliceStruct(&s) // 打印切片結(jié)構(gòu)
}

// 底層數(shù)組變化
func case2(s []int) {
    s = append(s, 0) // 擴容切片
    s[1] = 1         // 修改切片中的元素
    PrintSliceStruct(&s) // 打印切片結(jié)構(gòu)
}

func PrintSliceStruct(s *[]int) {
    // 將切片轉(zhuǎn)換成 reflect.SliceHeader
    ss := (*reflect.SliceHeader)(unsafe.Pointer(s))

    // 打印切片的底層結(jié)構(gòu)
    fmt.Printf("slice struct: %+v, slice is %v\n", ss, *s)
}

關(guān)鍵點:

case1 函數(shù):

在 case1 中、你傳入一個長度為 5 的切片 s、并修改切片中的元素。

切片在函數(shù)內(nèi)的操作是對原切片的修改、因此底層數(shù)組沒有發(fā)生變化、切片的容量、長度仍然相同。

打印的 slice struct 的 Data、Len 和 Cap 字段顯示的是切片的原始底層數(shù)據(jù)結(jié)構(gòu)。

case2 函數(shù):

在 case2 中、你向切片添加一個元素(通過 append 操作)、這將可能導(dǎo)致切片的底層數(shù)組擴容。

因為 append 操作在超出當(dāng)前容量時會觸發(fā)擴容、所以 s 的底層數(shù)組會發(fā)生變化、容量也可能增加。

 case2 中、s 被賦值為 append(s, 0)、這將導(dǎo)致原有切片 s 的底層數(shù)組被擴展、并且一個新的數(shù)組被分配給 s(s 指向的是新的底層數(shù)組)

打印時會看到 slice struct 中的 Data 指向一個新的地址、表示底層數(shù)組已經(jīng)發(fā)生了變化。

append(s, 0) 函數(shù)會檢查切片 s 是否有足夠的容量來存儲新的元素。如果切片的容量不足、append 函數(shù)會分配一個新的更大的數(shù)組、并復(fù)制舊數(shù)組的內(nèi)容到新數(shù)組中、然后將新元素添加到新數(shù)組的末尾、并更新切片的指針以指向包含新元素的新底層數(shù)組。

3.3 截取切片

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    s := make([]int, 5)  // 創(chuàng)建一個長度為 5 的切片,默認(rèn)初始化為 [0 0 0 0 0]
    
    case1(s)  // 調(diào)用 case1,修改切片內(nèi)容
    case2(s)  // 調(diào)用 case2,修改切片并改變底層數(shù)組
    case3(s)  // 調(diào)用 case3,截取切片并改變其長度
    case4(s)  // 調(diào)用 case4,截取切片的部分元素
    PrintSliceStruct(&s)  // 最后打印切片的底層結(jié)構(gòu)
}

// case1:修改切片元素、底層數(shù)組不變
func case1(s []int) {
    s[1] = 1  // 修改切片中的第二個元素,s[1] = 1
    PrintSliceStruct(&s)  // 打印修改后的切片底層結(jié)構(gòu)
}

// case2:重新賦值為新的切片
func case2(s []int) {
    s = s[:]  // 這里實際上并沒有改變切片的內(nèi)容、它只是重新賦值為原切片的一個新引用。
    PrintSliceStruct(&s)  // 打印新的切片底層結(jié)構(gòu)
}

// case3:截取切片、底層數(shù)組不變
func case3(s []int) {
    s = s[:len(s)-1]  // 截取切片、去掉最后一個元素、新的切片長度為 4
    PrintSliceStruct(&s)  // 打印截取后的切片底層結(jié)構(gòu)
}

// case4:截取切片的部分元素、底層數(shù)組不變
func case4(s []int) {
    sl := s[1:2]  // 截取 s[1:2],即取出切片中索引為 1 的元素
    PrintSliceStruct(&sl)  // 打印截取后的新切片底層結(jié)構(gòu)
}

// PrintSliceStruct 打印切片的底層結(jié)構(gòu)
func PrintSliceStruct(s *[]int) {
    // 將切片的指針轉(zhuǎn)換為 reflect.SliceHeader 結(jié)構(gòu)體,通過 unsafe.Pointer 獲取底層數(shù)據(jù)
    ss := (*reflect.SliceHeader)(unsafe.Pointer(s))
    
    // 打印切片的底層數(shù)據(jù)結(jié)構(gòu)、包括:指向底層數(shù)組的內(nèi)存地址、切片的長度和容量
    fmt.Printf("slice struct: %+v, slice is %v\n", ss, *s)
}

總結(jié):

切片操作的影響

修改切片元素不會改變底層數(shù)組的地址。

重新賦值切片并沒有改變底層數(shù)組、除非涉及擴容(例如 append)。

截取切片時、底層數(shù)組不變、切片的長度和容量可能會變化。

3.4 刪除元素

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    // 創(chuàng)建一個包含5個整數(shù)的切片
    s := []int{0, 1, 2, 3, 4}

    // 打印切片的底層結(jié)構(gòu)
    PrintSliceStruct(&s)

    // 刪除切片中的最后一個元素,正確的做法是通過切片截取
    _ = s[4]                      // 訪問并丟棄切片中的最后一個元素
    s1 := append(s[:1], s[2:]...) // 刪除元素 s[1],
 //s[:1](即切片 [0])和 s[2:](即切片 [2, 3, 4])拼接在一起。


    // 打印修改后的切片
    fmt.Println(s)  // [0 2 3 4 4]
    fmt.Println(s1) // [0, 2, 3, 4]

    // 打印切片底層結(jié)構(gòu)
    PrintSliceStruct(&s)
    PrintSliceStruct(&s1)

    // 訪問切片的元素
    s = s[:4] // 截取切片、刪除最后一個元素
    _ = s[3]  // 訪問切片中的最后一個元素(索引為3的元素)
}

// 打印切片的底層結(jié)構(gòu)
func PrintSliceStruct(s *[]int) {
    // 將切片轉(zhuǎn)換為 reflect.SliceHeader
    ss := (*reflect.SliceHeader)(unsafe.Pointer(s))

    // 打印切片的底層結(jié)構(gòu)
    fmt.Printf("slice struct: %+v, slice is %v\n", ss, *s)
}

在 Go 中、切片操作需要特別注意切片的索引和截取。訪問切片中的元素時要小心類型不匹配(例如不能將一個切片元素賦值給切片)。

控制臺輸出:

slice struct: &{Data:1374390755328 Len:5 Cap:5}, slice is [0 1 2 3 4]
[0 2 3 4 4]
[0 2 3 4]
slice struct: &{Data:1374390755328 Len:5 Cap:5}, slice is [0 2 3 4 4]
slice struct: &{Data:1374390755328 Len:4 Cap:5}, slice is [0 2 3 4]

打印原切片 s 時、它仍然指向原底層數(shù)組(長度為 5、容量為 5)、而且由于 s[4] 在內(nèi)存中并沒有被移除、原底層數(shù)組中的最后一個元素 4 被保留、因此 s 顯示為 [0 2 3 4 4]。

簡而言之s 顯示為 [0 2 3 4, 4] 是因為原始切片的底層數(shù)組并沒有被修改、而 append 操作生成了一個新的切片(s1)并分配了新的底層數(shù)組。所以s 中仍然包含原數(shù)組中的所有元素、最后一個 4 仍然存在。

為什么 s變成了 [0, 2, 3, 4, 4]

append 會根據(jù)切片的容量決定是否會使用原來的底層數(shù)組。如果原切片的容量足夠大、append 就會直接修改原切片。

在這段代碼中、由于原始切片 s 的容量足夠大(原始切片 s 的容量為 5)、append 仍然修改了原始切片 s 的內(nèi)容。切片的 s 和 s1 都指向相同的底層數(shù)組。

重點:

原切片 s 的容量沒有改變:s 底層的數(shù)組仍然包含原來 s 的所有元素。

append 沒有重新分配新的底層數(shù)組:由于原切片的容量足夠、所以 append 在修改原底層數(shù)組時、并沒有創(chuàng)建新的底層數(shù)組。因此原始切片 s 中的 4 仍然存在。

修改后 s 中的元素為 [0, 2, 3, 4, 4]:雖然你刪除了 s[1] 這個元素、但 append 使得 s 的底層數(shù)組沒有發(fā)生變化,因此原始的 4 元素仍然保留在切片中。

結(jié)論:

append 操作有時會創(chuàng)建新的底層數(shù)組(如果容量不足)、但如果原切片的容量足夠、append 直接修改原切片的底層數(shù)組。在這種情況下原切片 s 會保持原來的容量和數(shù)據(jù)、導(dǎo)致 s 顯示為 [0, 2, 3, 4, 4],即最后一個 4 保留下來了。

3.5 新增元素

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    case1()
    case2()
    case3()
}

// case1 函數(shù)展示了使用 append 在切片末尾添加元素的行為
func case1() {
    // 創(chuàng)建一個長度為 3,容量為 3 的切片
    s1 := make([]int, 3, 3)
    // 向切片添加一個元素 1,append 返回一個新的切片
    s1 = append(s1, 1)
    // 打印切片的底層結(jié)構(gòu)
    PrintSliceStruct(&s1) //1
}

// case2 函數(shù)展示了在原切片上使用 append 并打印切片結(jié)構(gòu)的變化
func case2() {
    // 創(chuàng)建一個長度為 3,容量為 4 的切片
    s1 := make([]int, 3, 4)
    // 向切片添加一個元素 1,append 會擴展切片的長度
    s2 := append(s1, 1)
    // 打印原切片 s1 和新切片 s2 的底層結(jié)構(gòu)
    PrintSliceStruct(&s1)//2
    PrintSliceStruct(&s2)//3
}

// case3 函數(shù)與 case2 類似,展示了切片長度、容量變化的行為
func case3() {
    // 創(chuàng)建一個長度為 3,容量為 3 的切片
    s1 := make([]int, 3, 3)
    // 向切片添加一個元素 1,append 返回一個新的切片
    s2 := append(s1, 1)
    // 打印原切片 s1 和新切片 s2 的底層結(jié)構(gòu)
    PrintSliceStruct(&s1)//4
    PrintSliceStruct(&s2)//5
}

// PrintSliceStruct 打印切片的底層結(jié)構(gòu)
func PrintSliceStruct(s *[]int) {
    // 使用 reflect 和 unsafe 包將切片轉(zhuǎn)換成 reflect.SliceHeader 結(jié)構(gòu)體
    ss := (*reflect.SliceHeader)(unsafe.Pointer(s))

    // 打印切片的底層結(jié)構(gòu)
    fmt.Printf("slice struct: %+v, slice is %v\n", ss, *s)
}

控制臺輸出

slice struct: &{Data:1374390755328 Len:4 Cap:6}, slice is [0 0 0 1]
slice struct: &{Data:1374390779936 Len:3 Cap:4}, slice is [0 0 0]
slice struct: &{Data:1374390779936 Len:4 Cap:4}, slice is [0 0 0 1]
slice struct: &{Data:1374390673552 Len:3 Cap:3}, slice is [0 0 0]
slice struct: &{Data:1374390755376 Len:4 Cap:6}, slice is [0 0 0 1]

case1:

使用 make([]int, 3, 3) 創(chuàng)建了一個長度為 3,容量為 3 的切片 s1,初始內(nèi)容為 [0, 0, 0]。

然后append(s1, 1) 會將元素 1 添加到切片的末尾、生成一個新的切片并返回。由于容量是 3、append 會自動擴容新的切片長度是 4

最后調(diào)用 PrintSliceStruct 打印 s1 切片的底層結(jié)構(gòu)。

case2:

make([]int, 3, 4) 創(chuàng)建了一個長度為 3、容量為 4 的切片 s1

使用 append(s1, 1) 向切片添加元素 1、生成一個新切片 s2。由于 s1 的容量已足夠、不會觸發(fā)擴容。

通過 PrintSliceStruct 打印切片 s1 和 s2 的底層結(jié)構(gòu)。

s3 和 s4 參考上面

3.6 操作原來切片會影響新的切片嗎

在 Go 中、切片是引用類型,這意味著當(dāng)你創(chuàng)建一個新切片時,它實際上可能會指向同一個底層數(shù)組。因此,如果你修改了原切片(比如通過 append 或其他操作),它可能會影響到新切片,特別是在底層數(shù)組沒有被重新分配的情況下。

切片和底層數(shù)組

切片(slice)是一個非常輕量級的抽象,它包含了三個部分:指向底層數(shù)組的指針、切片的長度和切片的容量。

當(dāng)你對切片進行操作時(例如使用 append、copy 或直接修改),這些操作通常會影響到底層數(shù)組。

如果多個切片引用同一個底層數(shù)組,改變其中一個切片的內(nèi)容可能會影響到其他切片,尤其是在沒有擴容時。

append操作

當(dāng)使用 append 函數(shù)時、如果切片的容量足夠、append 會直接在原底層數(shù)組上操作、不會創(chuàng)建新的底層數(shù)組。在這種情況下、修改原切片的內(nèi)容會影響到新切片、因為它們指向相同的底層數(shù)組。

如果容量不足、append 會創(chuàng)建一個新的底層數(shù)組、并將原切片的數(shù)據(jù)復(fù)制到新數(shù)組中、這時原切片和新切片就指向不同的底層數(shù)組了、它們互不影響。

例子:

沒有擴容:

s1 := []int{1, 2, 3}
s2 := s1      // s2 指向與 s1 相同的底層數(shù)組
s1[0] = 100   // 修改 s1 中的第一個元素
fmt.Println(s1) // 輸出 [100, 2, 3]
fmt.Println(s2) // 輸出 [100, 2, 3]

這里s1 和 s2 指向相同的底層數(shù)組,因此修改 s1 會影響到 s2。

擴容時:

s1 := []int{1, 2, 3}
s2 := append(s1, 4)  // s2 創(chuàng)建了新的底層數(shù)組
s1[0] = 100          // 修改 s1 中的第一個元素
fmt.Println(s1) // 輸出 [100, 2, 3]
fmt.Println(s2) // 輸出 [1、2、3、 4]

這里s2 創(chuàng)建了一個新的底層數(shù)組,因此修改 s1 不會影響 s2。

結(jié)論:

修改原切片會影響新切片:如果新切片是通過引用原切片的底層數(shù)組創(chuàng)建的(沒有觸發(fā)擴容)、修改原切片的內(nèi)容會影響到新切片。

擴容時不影響:如果 append 或其他操作導(dǎo)致了擴容、原切片和新切片就會指向不同的底層數(shù)組、互不影響。

4.字節(jié)面試題

下面這道題的輸出是什么

package main

import "fmt"

func main() {

    // 定義一個匿名函數(shù) doAppend,用來執(zhí)行 append 操作并打印切片的長度和容量
        doAppend := func(s []int) {
                s = append(s, 1)         // 向切片中添加元素 1
                printLengthAndCapacity(s) // 打印切片的長度和容量
        }

    // 創(chuàng)建一個長度為 8,容量為 8 的切片 s
        s := make([]int, 8, 8)
    
    // 傳遞 s 的前 4 個元素(即 s[:4])到 doAppend
        doAppend(s[:4])  // 只傳遞前4個元素的切片

    // 打印原始切片 s 的長度和容量
        printLengthAndCapacity(s)

    // 傳遞整個切片 s 到 doAppend
        doAppend(s)
    
    // 打印原始切片 s 的長度和容量
       printLengthAndCapacity(s)
}

func printLengthAndCapacity(s []int) {
    fmt.Println() 
    fmt.Printf("len=%d cap=%d \n", len(s), cap(s))  // 打印切片的長度和容量
}

len(s) 是切片的長度。

cap(s) 是切片的容量、表示切片底層數(shù)組的大小。

len=5 cap=8 len=8 cap=8 len=9 cap=16 len=8 cap=8

調(diào)用 doAppend(s[:4]):

s[:4] 是 s 切片的前 4 個元素、創(chuàng)建一個新的切片 [0, 0, 0, 0],長度為 4、容量為 8(因為它引用的是原切片的底層數(shù)組)。

在 doAppend 中、執(zhí)行 append(s, 1)、這會向切片添加一個元素 1、導(dǎo)致切片的長度變?yōu)?5、容量保持為 8(因為它沒有觸發(fā)擴容)。

打印結(jié)果為:len=5 cap=8。

調(diào)用 doAppend(s)

這次傳遞整個 s 切片、長度為 8、容量為 8。

執(zhí)行 append(s 1)、這會向切片 s 添加一個元素 1。因為 s 的容量是 8、不能再容納更多元素、因此會觸發(fā)擴容、新的底層數(shù)組的容量將是原來的 2 倍、即 16、長度變?yōu)?9。

打印結(jié)果為:len=9 cap=16。

注意:

append 創(chuàng)建了一個新的底層數(shù)組、并返回了一個新的切片。如果你不把返回的新切片賦值回 s、原始切片 s 不會改變、仍然指向舊的底層數(shù)組。

由于 append(s 1) 返回的是一個新的切片、但并沒有將它賦值回 s、所以原始切片 s 的長度和容量沒有變化、仍然是 len=8 和 cap=8

如果改成將新切片賦值回s

package main

import "fmt"

func main() {
    // 定義一個匿名函數(shù) doAppend 用于向切片添加元素
    doAppend := func(s []int) {
       s = append(s, 1)          // 向切片中添加元素 1
       printLengthAndCapacity(s) // 打印切片的長度和容量
    }

    // 定義一個匿名函數(shù) doAppend 用于向切片添加元素
    doAppends := func(s []int) []int {
       s = append(s, 1) // 使用 append 向切片添加一個元素 1
       printLengthAndCapacity(s)
       return s
    }

    // 創(chuàng)建一個長度為 8,容量為 8 的切片 s
    s := make([]int, 8, 8)

    // 傳遞前 4 個元素的切片
    doAppend(s[:4]) // 只傳遞前4個元素的切片
    printLengthAndCapacity(s)

    // 傳遞整個切片 s
    s = doAppends(s) // 將返回的新切片賦值回 s
    printLengthAndCapacity(s)
}

func printLengthAndCapacity(s []int) {
    fmt.Println()
    fmt.Printf("len=%d cap=%d \n", len(s), cap(s))
}

len=5 cap=8 
len=8 cap=8 
len=9 cap=16 
len=9 cap=16 

到此這篇關(guān)于Go語言Slice切片底層的實現(xiàn)的文章就介紹到這了,更多相關(guān)Go語言Slice切片內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言題解LeetCode599兩個列表的最小索引總和

    Go語言題解LeetCode599兩個列表的最小索引總和

    這篇文章主要為大家介紹了Go語言題解LeetCode599兩個列表的最小索引總和示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Golang?strings包常用字符串操作函數(shù)

    Golang?strings包常用字符串操作函數(shù)

    Golang?中的字符串統(tǒng)一使用?UTF-8?(屬于Unicode編碼的一種實現(xiàn)方式)進行編碼,本篇文章將結(jié)合具體實例對常用的字符串操作函數(shù)進行介紹,感興趣的可以了解一下
    2021-12-12
  • go程序員日常開發(fā)效率神器匯總

    go程序員日常開發(fā)效率神器匯總

    這篇文章主要介紹了go程序員開發(fā)效率神器包含了go常用開發(fā)工具,go調(diào)試工具,go常用網(wǎng)站,golang常用庫,需要的朋友可以參考下
    2022-11-11
  • go結(jié)構(gòu)體嵌套的切片數(shù)組操作

    go結(jié)構(gòu)體嵌套的切片數(shù)組操作

    這篇文章主要介紹了go結(jié)構(gòu)體嵌套的切片數(shù)組操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 詳解Go語言實現(xiàn)線性查找算法和二分查找算法

    詳解Go語言實現(xiàn)線性查找算法和二分查找算法

    線性查找又稱順序查找,它是查找算法中最簡單的一種。二分查找,也稱折半查找,相比于線性查找,它是一種效率較高的算法。本文將用Go語言實現(xiàn)這兩個查找算法,需要的可以了解一下
    2022-12-12
  • Go channel實現(xiàn)原理分析

    Go channel實現(xiàn)原理分析

    Channel是go語言內(nèi)置的一個非常重要的特性,也是go并發(fā)編程的兩大基石之一,下面這篇文章主要給大家介紹了關(guān)于Go中channel的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • Golang實現(xiàn)SSH、SFTP操作小結(jié)

    Golang實現(xiàn)SSH、SFTP操作小結(jié)

    在日常的一些開發(fā)場景中,我們需要去和遠程服務(wù)器進行一些通信,本文主要介紹了Golang實現(xiàn)SSH、SFTP操作小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2024-04-04
  • Golang多線程刷票的實現(xiàn)代碼

    Golang多線程刷票的實現(xiàn)代碼

    這篇文章主要介紹了Golang多線程刷票的相關(guān)資料,這里實現(xiàn)刷票的功能,對于投票,刷票的很方便,并附實現(xiàn)代碼,需要的朋友可以參考下
    2017-07-07
  • Go語言通過Luhn算法驗證信用卡卡號是否有效的方法

    Go語言通過Luhn算法驗證信用卡卡號是否有效的方法

    這篇文章主要介紹了Go語言通過Luhn算法驗證信用卡卡號是否有效的方法,實例分析了Luhn算法的原理與驗證卡號的使用技巧,需要的朋友可以參考下
    2015-03-03
  • GoLang 逃逸分析的機制詳解

    GoLang 逃逸分析的機制詳解

    這篇文章主要介紹了GoLang-逃逸分析的機制詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02

最新評論