偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Go 語言的 Array 和 Slice

開發(fā) 后端
Go 語言的數(shù)組和其他語言一樣, 沒有什么特別的地方, 就是一段以元素類型(如int)為單位的連續(xù)內(nèi)存空間。數(shù)組創(chuàng)建時, 被初始化為元素類型的零值.

先拋出幾個問題

  1. 聲明一個 slice 并賦值為 nil, 如 var slice []int = nil,此時 len(slice) 的運行結(jié)果是什么?
  2. func(arr []int) 和 func(arr [10]int) 兩個函數(shù)內(nèi)部都對 arr 進行修改, 對外面的值(作為參數(shù)的數(shù)據(jù))是否造成影響?
  3. 創(chuàng)建一個 slice := make([]int, 5, 10), 然后 slice[8] 和 slice[:8] 的運行結(jié)果是什么?
  4. 下面兩段代碼的輸出結(jié)果是什么
slice := []int{1, 2, 3, 4, 5}
slice2 := append(slice[:3], 6, 7)
fmt.Println(slice)
fmt.Println(slice2)
slice := []int{1, 2, 3, 4, 5}
slice2 := append(slice[:3], 6, 7, 8) // 多追加一個數(shù)字 8, 這是唯一的不同
fmt.Println(slice)
fmt.Println(slice2)

如果上面的問題都能很輕松回答上來, 可以直接關(guān)閉文章了。

為了方便, 下面的描述均以 int 作為元素類型說明

數(shù)組 Array

先說一下數(shù)組, 的確在 Go 語言中, 因為 slice 的存在, 使得 array 的出場率不高。但想要很好地理解 slice, 還是要先要了解 array.

數(shù)組的聲明

Go 語言的數(shù)組和其他語言一樣, 沒有什么特別的地方, 就是一段以元素類型(如int)為單位的連續(xù)內(nèi)存空間。數(shù)組創(chuàng)建時, 被初始化為元素類型的零值.

聲明舉例:

var arr [10]int  // 長度為 10 的數(shù)組, 默認所有元素是 0
arr := [...]int{1, 2, 3} // 長度由初始化元素個數(shù)指定, 這里長度是 3
arr := [...]int{11: 3} // 長度為 11 的數(shù)組, arr[11] 初始化為 3, 其他為 0
arr := [5]int{1,2} // 長度為 5 的數(shù)組, 前兩位初始化為 1, 2
arr := [5]int{1,2} // 長度為 5 的數(shù)組, 前兩位初始化為 1, 2
arr := [...]int{1: 23, 2, 3: 22} // 長度為 4 的數(shù)組, 初始化為 [0 23 2 22]

[] 內(nèi)設(shè)定數(shù)組長度, 寫成 ... 表示長度由后面的初始化值決定.

數(shù)組初始化的完整寫法是 {1:23, 2:8, 3:12}, 只不過可以省略 index 寫成 {23, 8, 12}, index 自動從 0 開始累加, 最大的 index 值決定數(shù)組長度.

如 {5: 10, 11, 12, 6: 100} 是非法的, 因為它會被轉(zhuǎn)換成 {5: 10, 6: 11, 7: 12, 6: 100}, 會出現(xiàn)編譯錯誤 duplicate index in array literal: 6.

長度為 0 的數(shù)組

比較特別的就是 [0]int, 長度為 0 的數(shù)組. 這種不占有任何內(nèi)存空間的數(shù)據(jù)類型實際上是無意義的, 所以 Go 語言對此類數(shù)據(jù)特殊處理了一下, 此外還包括 struct{}, [10]struct{} 等.

看一個例子:

var (
a [0]int
b struct{}
c [0]struct {
Value int64
}
d [10]struct{}
e = new([10]struct{}) // new 返回的就是指針
f byte
)
fmt.Printf("%p, %p, %p, %p, %p, %p", &a, &b, &c, &d, e, &f)
// 0x1127a88, 0x1127a88, 0x1127a88, 0x1127a88, 0x1127a88, 0xc42000e280

前 5 個變量的內(nèi)存地址一樣, 第 6 個變量 f 有一個真實可用的內(nèi)存. 也就是說 Go 并沒有為 [0]int 和 struct{} 這類數(shù)據(jù)真正分配地址空間, 而是統(tǒng)一使用同一個地址空間.

這類數(shù)據(jù)結(jié)構(gòu)在 map 中經(jīng)常應(yīng)用, 比如 map[string]struct{}. 聲明這樣一個 map 類型來標記某個 key 是否存在. 在 key 值很多的情況下, 要比 map[string]bool 之類的結(jié)構(gòu)節(jié)約很多內(nèi)存, 同時也減小 GC 壓力.

數(shù)組作為函數(shù)參數(shù)

文章最開始的問題中提到, func(arr [3]int) 內(nèi)部對 arr 進行修改是否會影響外面的實際值. 答案是不會.

因為一個數(shù)組作為參數(shù)時, 會拷貝一份副本作為參數(shù), 函數(shù)內(nèi)部操作的數(shù)組與外界數(shù)組, 在內(nèi)存中根本就不是同一個地方. 是值傳遞不是引用傳遞, 這點可能和某些語言不同.

看下面代碼:

array := [3]int{1, 2, 3}
func(innerArray [3]int) {
innerArray[0] = 8
fmt.Printf("%p: %v\n", &innerArray, innerArray)
}(array)
fmt.Printf("%p: %v\n", &array, array)
// 0xc42000a2e0: [8 2 3]
// 0xc42000a2c0: [1 2 3]

函數(shù)內(nèi)外, 數(shù)組的內(nèi)存地址都不一樣, 自然不會有影響.

如果你想讓函數(shù)直接修改, 可以使用指針, 即 func(arr *[3]int).

切片 Slice

slice 通常用來表示一個變長序列, 也是基于數(shù)組實現(xiàn)的??聪聢D:

圖中 Q2 和 summer 是 slice, 實際就是對數(shù)組 months 引用, 只是記錄了引用了數(shù)組中的那些元素.

再看一下 slice 在 Go 內(nèi)部的定義.

type slice struct {
array unsafe.Pointer // 被引用的數(shù)組中的起始元素地址
len int // 長度
cap int // 最大長度
}

我們對 slice 的讀寫, 實際上操作的都是它所指向的數(shù)組.

看到了上面的 slice 數(shù)據(jù)結(jié)構(gòu), 自然就知道了以下兩點:

值為 nil 的 slice 變量的 len 和 cap 都是 0. 雖然它沒有指向具體某個數(shù)組(slice.array 為空), 但是它的 slice.len 和 slice.cap 默認就是 0.

func(arr []int) 這種函數(shù)對參數(shù) arr 的修改, 會影響到外面數(shù)值, 因為函數(shù)內(nèi)部操作的內(nèi)存與外界是同一個. 這是 slice 和 array 的主要區(qū)別之一.

slice 越界

slice 是可伸縮變長的, 導(dǎo)致很多人誤以為 slice 是不會越界的, 下面我們來闡述下幾種越界情況.

以上圖中右側(cè)的 summer 為例, summer[4] = "hello" 肯定會出現(xiàn) index out of range 的 panic 信息, 盡管 cap(summer) = 7, 但 summer[4] 超出了 len(summer) = 3 的范圍.

再看下面這個例子:

arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(arr[:3:5][:4]) // [1 2 3 4]
fmt.Println(arr[:3:5][:8]) // panic: runtime error: slice bounds out of range

arr[:3:5] 基于 arr 創(chuàng)建一個 slice, len 是 3, cap 是 5; 然后再在這個 slice 的基礎(chǔ)上分別創(chuàng)建一個 len = 4 和 len = 8 的 slice. 前者運行正常, 后者因超出 cap = 5 范圍而 panic, 盡管后者實際想要的內(nèi)存并沒有超出 arr 數(shù)組范圍.

對 slice 的操作記住兩點:

  1. 數(shù)據(jù)直接訪問(slice[index])時, index 值不能超過 len(slice) 范圍
  2. 創(chuàng)建切片(slice[start:end])時, start 和 end 指定的區(qū)間不能超過 cap(slice) 范圍

所以, 文章開頭的第 3 個問題, slice[8] 會 panic, 而 slice[:8] 正常返回.

append 函數(shù)

很多人以為 slice 是可以自動擴充的, 估計都是 append 函數(shù)誤導(dǎo)的. 其實 slice 并不會自己自動擴充, 而是 append 數(shù)據(jù)時, 該函數(shù)如果發(fā)現(xiàn)超出了 cap 限制自動幫我們擴的.

當執(zhí)行 append(slice, v1, v2) 時, append 函數(shù)會先檢查執(zhí)行結(jié)果的長度是否會超出 cap(slice).

如果超出, 就先 make 一個更長的 slice, 然后把整個 slice 都 copy 到新 slice 中, 再進行 append.

如果沒超, 直接以 len(slice) 為起始點進行追加, len(slice) 會隨著 append 操作不斷擴大, 直到達到 cap(slice) 進行擴充.

建議使用者盡可能的避免讓 append 自動為你擴充內(nèi)存. 一個是因為擴充時會出現(xiàn)一次內(nèi)存拷貝, 二是因為 append 并不知道需要擴充多少, 為了避免頻繁擴充, 它會擴充到 2 * cap(slice) 長度. 而有時我們并不需要那么多內(nèi)存.

所以在使用 slice 時, 最好不要不 make, 直接 append 讓其自己擴充; 而是先 make([]int, 0, capValue) 準備一塊內(nèi)存, capValue 需要自己估計下, 盡可能確保足夠用就好.

責任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2012-06-15 09:56:40

2021-07-08 23:53:44

Go語言拷貝

2021-05-14 08:58:18

非線性安全Go

2023-11-28 11:44:54

Go切片

2019-12-05 12:57:53

slice編程函數(shù)

2025-03-04 00:25:55

Go開發(fā)者切片

2017-08-31 11:28:47

Slice底層實現(xiàn)

2024-09-03 09:45:36

2018-04-19 14:54:12

2023-03-29 08:03:53

2023-03-13 13:36:00

Go擴容切片

2025-06-04 02:15:55

Go語言Slice

2023-12-30 18:35:37

Go識別應(yīng)用程序

2023-11-21 15:46:13

Go內(nèi)存泄漏

2023-05-24 09:31:51

CGo

2012-10-08 09:25:59

GoGo語言開發(fā)語言

2022-01-10 13:01:32

指針Struct內(nèi)存

2018-03-12 22:13:46

GO語言編程軟件

2020-12-29 06:44:18

GoScala編程語言

2013-08-20 13:22:35

PythonGo編程語言
點贊
收藏

51CTO技術(shù)棧公眾號