首頁  >  文章  >  後端開發  >  Go 語言中 nil 切片、非 nil 切片、空切片

Go 語言中 nil 切片、非 nil 切片、空切片

WBOY
WBOY轉載
2024-02-08 21:18:081250瀏覽

Go 语言中 nil 切片、非 nil 切片、空切片

php小編小新為你帶來了關於Go語言中的切片類型的介紹。在Go語言中,切片有三種狀態:nil切片、非nil切片和空切片。這三種切片狀態在使用時有不同的意義和特徵。了解這些切片類型的區別,將有助於我們更好地理解和使用Go語言中的切片功能。接下來,讓我們一起來探索這三種切片類型的具體特徵和用法。

問題內容

我是 Go 程式設計的新手。我在Go程式設計書中讀到,切片由三個部分組成:指向陣列的指標、長度和容量。

我對以下內容感到困惑:

  • nil 切片(切片沒有指向的底層數組,len = 0,cap=0)
  • 僅 len = 0、cap = 0 的非零切片
  • 空切片。

誰能告訴我 nil 和空切片是否是同一件事? 如果兩者不同,那麼請告訴我這兩者有什麼不同?如何測試切片是否為空?另外,指針在長度和容量為零的非零切片中保存什麼值?

解決方法

可觀察的行為

nil 和空切片(容量為 0)並不相同,但它們的可觀察行為是相同的(幾乎始終)。我的意思是:

  • 您可以將它們傳遞給內建 len()#cap()# 函數
  • 您可以 for range 覆寫它們(將是 0 次迭代)
  • 您可以對它們進行切片(只要不違反規範:切片表達式中概述的限制;因此結果也將是一個空切片)
  • 由於它們的長度為 0,因此您無法變更其內容(附加價值會建立新的切片值)

查看這個簡單的範例(一個 nil 切片和 2 個非 nil 空切片):

var s1 []int         // nil slice
s2 := []int{}        // non-nil, empty slice
s3 := make([]int, 0) // non-nil, empty slice

fmt.Println("s1", len(s1), cap(s1), s1 == nil, s1[:], s1[:] == nil)
fmt.Println("s2", len(s2), cap(s2), s2 == nil, s2[:], s2[:] == nil)
fmt.Println("s3", len(s3), cap(s3), s3 == nil, s3[:], s3[:] == nil)

for range s1 {}
for range s2 {}
for range s3 {}

輸出(在 Go Playground 上嘗試):

s1 0 0 true [] true
s2 0 0 false [] false
s3 0 0 false [] false

(請注意,對nil 切片進行切片會產生nil 切片,對非nil 切片進行切片會產生非nil 切片。)

除了例外之外,您只能透過將切片值與預先宣告的識別碼 nil 進行比較來區分,它們在其他方面的行為都是相同的。 但請注意,許多軟體包確實會將切片與nil 進行比較,並且可能會基於此進行不同的操作(例如encoding/ jsonfmt 套件)。

唯一的差異是將切片轉換為陣列指標(已新增至Go 1.17 中的語言)。將非 nil 切片轉換為陣列指標將產生非 nil 指針,將 nil 切片轉換為陣列指標將產生 nil 指標。

要判斷切片是否為空,只需將其長度與 0 進行比較:len(s) == 0。無論它是 nil 切片或非 nil 切片,它是否具有正容量也並不重要;如果沒有元素,則為空。

s := make([]int, 0, 100)
fmt.Println("Empty:", len(s) == 0, ", but capacity:", cap(s))

列印(在 Go Playground 上嘗試):

Empty: true , but capacity: 100

底層

切片值由 reflect.SliceHeader 中定義的結構表示:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

對於 nil 切片,該結構將具有其零值,即其所有欄位都將為零值,即:0

如果非nil 切片的容量和長度等於0LenCap 字段,則很可能是 0,但Data 指標可能不是。它不會,這就是它與 nil 切片的區別。它將指向一個零大小的底層數組。

請注意,Go 規格允許大小為 0 的不同類型的值具有相同的記憶體位址。 規格:系統注意事項:大小和對齊保證:

让我们检查一下。为此,我们调用 unsafe 包的帮助,并“获取” reflect.SliceHeader 结构“视图” “我们的切片值:

var s1 []int
s2 := []int{}
s3 := make([]int, 0)

fmt.Printf("s1 (addr: %p): %+8v\n",
    &s1, *(*reflect.SliceHeader)(unsafe.Pointer(&s1)))
fmt.Printf("s2 (addr: %p): %+8v\n",
    &s2, *(*reflect.SliceHeader)(unsafe.Pointer(&s2)))
fmt.Printf("s3 (addr: %p): %+8v\n",
    &s3, *(*reflect.SliceHeader)(unsafe.Pointer(&s3)))

输出(在 Go Playground 上尝试一下):

s1 (addr: 0x1040a130): {Data:       0 Len:       0 Cap:       0}
s2 (addr: 0x1040a140): {Data: 1535812 Len:       0 Cap:       0}
s3 (addr: 0x1040a150): {Data: 1535812 Len:       0 Cap:       0}

我们看到了什么?

  • 所有切片(切片头)都有不同的内存地址
  • nil 切片具有 0 数据指针
  • s2s3 切片确实具有相同的数据指针,共享/指向相同的 0 大小的内存值

以上是Go 語言中 nil 切片、非 nil 切片、空切片的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除