成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go 語言的 Array 和 Slice

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

先拋出幾個問題

  1. 聲明一個 slice 并賦值為 nil, 如 var slice []int = nil,此時 len(slice) 的運行結果是什么?
  2. func(arr []int) 和 func(arr [10]int) 兩個函數內部都對 arr 進行修改, 對外面的值(作為參數的數據)是否造成影響?
  3. 創建一個 slice := make([]int, 5, 10), 然后 slice[8] 和 slice[:8] 的運行結果是什么?
  4. 下面兩段代碼的輸出結果是什么
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) // 多追加一個數字 8, 這是唯一的不同
fmt.Println(slice)
fmt.Println(slice2)

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

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

數組 Array

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

數組的聲明

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

聲明舉例:

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

[] 內設定數組長度, 寫成 ... 表示長度由后面的初始化值決定.

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

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

長度為 0 的數組

比較特別的就是 [0]int, 長度為 0 的數組. 這種不占有任何內存空間的數據類型實際上是無意義的, 所以 Go 語言對此類數據特殊處理了一下, 此外還包括 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 個變量的內存地址一樣, 第 6 個變量 f 有一個真實可用的內存. 也就是說 Go 并沒有為 [0]int 和 struct{} 這類數據真正分配地址空間, 而是統一使用同一個地址空間.

這類數據結構在 map 中經常應用, 比如 map[string]struct{}. 聲明這樣一個 map 類型來標記某個 key 是否存在. 在 key 值很多的情況下, 要比 map[string]bool 之類的結構節約很多內存, 同時也減小 GC 壓力.

數組作為函數參數

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

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

看下面代碼:

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]

函數內外, 數組的內存地址都不一樣, 自然不會有影響.

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

切片 Slice

slice 通常用來表示一個變長序列, 也是基于數組實現的。看下圖:

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

再看一下 slice 在 Go 內部的定義.

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

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

看到了上面的 slice 數據結構, 自然就知道了以下兩點:

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

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

slice 越界

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

以上圖中右側的 summer 為例, summer[4] = "hello" 肯定會出現 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 創建一個 slice, len 是 3, cap 是 5; 然后再在這個 slice 的基礎上分別創建一個 len = 4 和 len = 8 的 slice. 前者運行正常, 后者因超出 cap = 5 范圍而 panic, 盡管后者實際想要的內存并沒有超出 arr 數組范圍.

對 slice 的操作記住兩點:

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

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

append 函數

很多人以為 slice 是可以自動擴充的, 估計都是 append 函數誤導的. 其實 slice 并不會自己自動擴充, 而是 append 數據時, 該函數如果發現超出了 cap 限制自動幫我們擴的.

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

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

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

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

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

責任編輯:姜華 來源: 今日頭條
相關推薦

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編程函數

2025-03-04 00:25:55

Go開發者切片

2017-08-31 11:28:47

Slice底層實現

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識別應用程序

2023-11-21 15:46:13

Go內存泄漏

2023-05-24 09:31:51

CGo

2012-10-08 09:25:59

GoGo語言開發語言

2022-01-10 13:01:32

指針Struct內存

2018-03-12 22:13:46

GO語言編程軟件

2020-12-29 06:44:18

GoScala編程語言

2013-08-20 13:22:35

PythonGo編程語言
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本激情视频中文字幕 | 久久久久久网站 | 日韩在线播放网址 | 欧美国产一区二区 | 欧美综合一区二区 | 国产精品视频免费观看 | 久久性 | 国产在线精品一区二区 | 91免费在线看 | 欧美日韩亚洲一区二区 | 久久高清 | 久操福利 | 日本精品一区二区三区视频 | 精品精品视频 | 欧美日韩精品专区 | 麻豆国产精品777777在线 | 精品一二区 | 亚洲欧美国产精品久久 | 国产精品片aa在线观看 | 一区二区三区在线 | 99re99| 国产成人精品网站 | 国产美女黄色 | 亚洲日韩中文字幕一区 | 久久国产欧美日韩精品 | 国产视频一区二区三区四区五区 | 一区二区三区四区国产 | 色综合99| 91中文| 日韩欧美精品一区 | 欧美精品在线播放 | 免费国产精品久久久久久 | 久久精品国产久精国产 | 国产精品视频网站 | 亚洲综合色网站 | av黄色免费在线观看 | 视频在线一区 | 久久久久久久久久久一区二区 | 狠狠婷婷综合久久久久久妖精 | 6080亚洲精品一区二区 | 男人天堂网av|