golang中的空slice案例
golang中允許對值為 nil 的 slice 添加元素
package main func main() { var s []int s = append(s, 1) }
運(yùn)行成功~
補(bǔ)充:golang slice 詳解
一、數(shù)組切片的使用
func main() { //1.基于數(shù)組創(chuàng)建數(shù)組切片 var array [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} var slice = array[1:7] //array[startIndex:endIndex] 不包含endIndex //2.直接創(chuàng)建數(shù)組切片 slice2 := make([]int, 5, 10) //3.直接創(chuàng)建并初始化數(shù)組切片 slice3 := []int{1, 2, 3, 4, 5, 6} //4.基于數(shù)組切片創(chuàng)建數(shù)組切片 slice5 := slice3[:4] //5.遍歷數(shù)組切片 for i, v := range slice3 { fmt.Println(i, v) } //6.len()和cap() var len = len(slice2) //數(shù)組切片的長度 var cap = cap(slice) //數(shù)組切片的容量 fmt.Println("len(slice2) =", len) fmt.Println("cap(slice) =", cap) //7.append() 會生成新的數(shù)組切片 slice4 := append(slice2, 6, 7, 8) slice4 = append(slice4, slice3...) fmt.Println(slice4) //8.copy() 如果進(jìn)行操作的兩個數(shù)組切片元素個數(shù)不一致,將會按照個數(shù)較小的數(shù)組切片進(jìn)行復(fù)制 copy(slice2, slice3) //將slice3的前五個元素復(fù)制給slice2 fmt.Println(slice2, slice3) }
二、數(shù)組切片數(shù)據(jù)結(jié)構(gòu)分析
數(shù)組切片slice的數(shù)據(jù)結(jié)構(gòu)如下,一個指向真實(shí)array地址的指針ptr,slice的長度len和容量cap
// slice 數(shù)據(jù)結(jié)構(gòu) type slice struct { array unsafe.Pointer len int cap int }
當(dāng)傳參時,函數(shù)接收到的參數(shù)是數(shù)組切片的一個復(fù)制,雖然兩個是不同的變量,但是它們都有一個指向同一個地址空間的array指針,當(dāng)修改一個數(shù)組切片時,另外一個也會改變,所以數(shù)組切片看起來是引用傳遞,其實(shí)是值傳遞。
三、append()方法解析
3.1 數(shù)組切片不擴(kuò)容的情況
運(yùn)行以下代碼思考一個問題:s1和s2是指向同一個底層數(shù)組嗎?
func main() { array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := array[:5] s2 := append(s1, 10) fmt.Println("s1 =", s1) fmt.Println("s2 =", s2) s2[0] = 0 fmt.Println("s1 =", s1) fmt.Println("s2 =", s2) }
輸出結(jié)果:
s1 = [1 2 3 4 5]
s2 = [1 2 3 4 5 10]
s1 = [0 2 3 4 5]
s2 = [0 2 3 4 5 10]
由第一行和第二行結(jié)果看來,似乎這是指向兩個不同的數(shù)組;但是當(dāng)修改了s2,發(fā)現(xiàn)s1也跟著改變了,這又表明二者是指向同一個數(shù)組。到底真相是怎樣的呢?
運(yùn)行以下代碼:
import ( "fmt" "unsafe" ) type Slice struct { ptr unsafe.Pointer // Array pointer len int // slice length cap int // slice capacity } func main() { array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := array[:5] s2 := append(s1, 10) s2[0] = 0 // 把slice轉(zhuǎn)換成自定義的 Slice struct slice1 := (*Slice)(unsafe.Pointer(&s1)) fmt.Printf("ptr:%v len:%v cap:%v \n", slice1.ptr, slice1.len, slice1.cap) slice2 := (*Slice)(unsafe.Pointer(&s2)) fmt.Printf("ptr:%v len:%v cap:%v \n", slice2.ptr, slice2.len, slice2.cap) }
輸出結(jié)果:
ptr:0xc04205e0a0 len:5 cap:20
ptr:0xc04205e0a0 len:6 cap:20
由結(jié)果可知:ptr指針存儲的是數(shù)組中的首地址的值,并且這兩個值相同,所以s1和s2確實(shí)是指向同一個底層數(shù)組。
但是,這兩個數(shù)組切片的元素不同,這個可以根據(jù)首地址和數(shù)組切片長度len來確定不同的數(shù)組切片應(yīng)該包含哪些元素,因?yàn)閟1和s2雖然指向同一個底層數(shù)組,但是二者的len不同。通過這個demo,也驗(yàn)證了數(shù)組切片傳參方式也是值傳遞。
3.2 數(shù)組切片擴(kuò)容的情況:
運(yùn)行以下代碼,思考與不擴(kuò)容情況的不同之處,以及為什么
func main() { s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} s2 := append(s1, 10) fmt.Println("s1 =", s1) fmt.Println("s2 =", s2) s2[0] = 0 fmt.Println("s1 =", s1) fmt.Println("s2 =", s2) }
輸出結(jié)果:
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [1 2 3 4 5 6 7 8 9 10]
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [0 2 3 4 5 6 7 8 9 10]
根據(jù)結(jié)果我們發(fā)現(xiàn),修改s2后,s1并未改變,這說明當(dāng)append()后,s1和s2并未指向同一個底層數(shù)組,這又是為什么呢?
同樣,我們接著運(yùn)行以下代碼:
import ( "fmt" "unsafe" ) type Slice struct { ptr unsafe.Pointer // Array pointer len int // slice length cap int // slice capacity } func main() { s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} s2 := append(s1, 10) fmt.Println("s1 =", s1) fmt.Println("s2 =", s2) s2[0] = 0 fmt.Println("s1 =", s1) fmt.Println("s2 =", s2) // 把slice轉(zhuǎn)換成自定義的 Slice struct slice1 := (*Slice)(unsafe.Pointer(&s1)) fmt.Printf("ptr:%v len:%v cap:%v \n", slice1.ptr, slice1.len, slice1.cap) slice2 := (*Slice)(unsafe.Pointer(&s2)) fmt.Printf("ptr:%v len:%v cap:%v \n", slice2.ptr, slice2.len, slice2.cap) }
輸出結(jié)果:
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [1 2 3 4 5 6 7 8 9 10]
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [0 2 3 4 5 6 7 8 9 10]
ptr:0xc04207a000 len:9 cap:9
ptr:0xc04207c000 len:10 cap:18
由結(jié)果可知:append()后,s1和s2確實(shí)指向了不同的底層數(shù)組,并且二者的數(shù)組容量cap也不相同了。
過程是這樣的:當(dāng)append()時,發(fā)現(xiàn)數(shù)組容量不夠用,于是開辟了新的數(shù)組空間,cap變?yōu)樵瓉淼膬杀叮瑂2指向了這個新的數(shù)組,所以當(dāng)修改s2時,s1不受影響
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
VSCode Golang dlv調(diào)試數(shù)據(jù)截?cái)鄦栴}及處理方法
這篇文章主要介紹了VSCode Golang dlv調(diào)試數(shù)據(jù)截?cái)鄦栴},本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06Goland項(xiàng)目使用gomod配置的詳細(xì)步驟
Goland是一個用于Go語言開發(fā)的IDE,Goland的項(xiàng)目結(jié)構(gòu)與Go語言的項(xiàng)目結(jié)構(gòu)相似,下面這篇文章主要給大家介紹了關(guān)于Goland項(xiàng)目使用gomod配置的詳細(xì)步驟,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04Gin與Mysql實(shí)現(xiàn)簡單Restful風(fēng)格API實(shí)戰(zhàn)示例詳解
這篇文章主要為大家介紹了Gin與Mysql實(shí)現(xiàn)簡單Restful風(fēng)格API示例詳解,有需要的朋友可以借鑒參考下希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法詳解
這篇文章主要介紹了Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法2024-01-01