題目來源: 深信服师妙、知乎诵肛、跟誰學(xué)
題目解析: GOALNG ROADMAP社區(qū)
答案 1:(溪尾)
數(shù)組長度是固定的,而切片是可變長的默穴≌荩可以把切片看作是對底層數(shù)組的封裝,每個切片的底層數(shù)據(jù)結(jié)構(gòu)中蓄诽,一定會包含一個數(shù)組薛训。數(shù)組可以被稱為切片的底層數(shù)組,切片也可以被看作對數(shù)組某一連續(xù)片段的引用仑氛。因此乙埃,Go 中切片屬于引用類型蝴猪,而數(shù)組屬于值類型,通過內(nèi)建函數(shù) len膊爪,可以取得數(shù)組和切片的長度。通過內(nèi)建函數(shù) cap嚎莉,可以得到數(shù)組和切片的容量米酬。但是數(shù)組的長度和容量是相等的,并且都不可變趋箩,而且切片容量是有變化規(guī)律的赃额。
答案 2:(行飛子)
數(shù)組和切片的關(guān)系:
切片一旦初始化, 切片始終與保存其元素的基礎(chǔ)數(shù)組相關(guān)聯(lián)叫确。因此跳芳,切片會和與其擁有同一基礎(chǔ)數(shù)組的其他切片共享存儲 ; 相比之下,不同的數(shù)組總是代表不同的存儲竹勉。
數(shù)組和切片的區(qū)別
切片的長度可能在執(zhí)行期間發(fā)生變化 飞盆,而數(shù)組的長度不能變化,可以把切片看成一個長度可變的數(shù)組次乓。
數(shù)組作為函數(shù)參數(shù)是進(jìn)行值傳遞的吓歇,函數(shù)內(nèi)部改變傳入的數(shù)組元素值不會影響函數(shù)外部數(shù)組的元素值; 切片作為函數(shù)的參數(shù)是進(jìn)行的指針傳遞票腰,函數(shù)內(nèi)部改變切片的值會影響函數(shù)外部的切片元素值城看。
數(shù)組可以比較,切片不能比較(對底層數(shù)組的引用)杏慰。
答案 3:(欒龍生)
1. Go 切片和 Go 數(shù)組
Go 切片测柠,又稱動態(tài)數(shù)組,它實(shí)際是基于數(shù)組類型做的一層封裝缘滥。
Go 數(shù)組
數(shù)組是內(nèi)置(build-in)類型,是一組同類型數(shù)據(jù)的集合轰胁,它是值類型,通過從 0 開始的下標(biāo)索引訪問元素值朝扼。在初始化后長度是固定的软吐,無法修改其長度。當(dāng)作為方法的參數(shù)傳入時將復(fù)制一份數(shù)組而不是引用同一指針吟税。數(shù)組的長度也是其類型的一部分凹耙,通過內(nèi)置函數(shù) len(array)獲取其長度。
Go 數(shù)組與像 C/C++等語言中數(shù)組略有不同肠仪,如下
- Go 中的數(shù)組是值類型肖抱,換句話說,如果你將一個數(shù)組賦值給另外一個數(shù)組异旧,那么意述,實(shí)際上就是將整個數(shù)組拷貝一份。因此,在 Go 中如果將數(shù)組作為函數(shù)的參數(shù)傳遞的話荤崇,那效率就肯定沒有傳遞指針高了拌屏。
- 數(shù)組的長度也是類型的一部分,這就說明
[10]int
和[20]int
不是同一種數(shù)據(jù)類型术荤。
Go 切片
Go 語言中數(shù)組的長度是固定的倚喂,且不同長度的數(shù)組是不同類型,這樣的限制帶來不少局限性瓣戚。
而切片則不同端圈,切片(slice)是一個擁有相同類型元素的可變長序列,可以方便地進(jìn)行擴(kuò)容和傳遞子库,實(shí)際使用時比數(shù)組更加靈活舱权,這也正是切片存在的意義。
切片是引用類型仑嗅,因此在當(dāng)傳遞切片時將引用同一指針宴倍,修改值將會影響其他的對象。
2. 切片底層
現(xiàn)在就來看一下 Go 語言切片的底層是什么樣子吧仓技!
Go 切片(slice)的實(shí)現(xiàn)可以在源碼包src/runtime/slice.go
中找到啊楚。在源碼中,slice 的數(shù)據(jù)結(jié)構(gòu)定義如下浑彰。
type slice struct {
array unsafe.Pointer //指向底層數(shù)組的指針
len int //切片長度
cap int //切片容量
}
可以看到恭理,組成 Go 切片的三元組分別為指向底層數(shù)組的指針,切片長度和切片容量郭变。
-
指向底層數(shù)組的指針
前面已經(jīng)提到颜价,切片實(shí)際是對數(shù)組的一層封裝。這個指針便是記錄其底層數(shù)組的地址诉濒,也正是切片開始的位置周伦。
-
切片長度
len
表示切片的長度,即切片中現(xiàn)存有效元素的個數(shù)未荒,它不能超過切片的容量专挪。可以通過len()
函數(shù)獲取切片長度片排。 -
切片容量
cap
表示切片的容量寨腔,即切片能存儲元素的多少,通常是從切片的起始元素到底層數(shù)組的最后一個元素間的元素個數(shù)率寡,當(dāng)切片容量不足時迫卢,便會觸發(fā) slice 擴(kuò)容∫惫玻可以通過cap()
函數(shù)獲取切片容量乾蛤。
下圖展示了一個 Go 切片的底層數(shù)據(jù)結(jié)構(gòu)每界,這個切片的長度為 3,容量為 6家卖。
3. 切片使用
- 切片定義方式
var a []int //nil切片眨层,和nil相等,一般用來表示一個不存在的切片
var b []int{} //空切片上荡,和nil不相等趴樱,一般用來表示一個空的集合
var c []int{1, 2, 3} //有3個元素的切片,len和cap都為3
var d = c[:2] //有2個元素的切片榛臼,len為2,cap為3
var e = c[:2:cap(c)] //有2個元素的切片窜司,len為2沛善,cap為3
var f = c[:0] //有0個元素的切片,len為0塞祈,cap為3
var g = make([]int, 3) //創(chuàng)建一個切片金刁,len和cap均為3
var h = make([]int, 3, 6) //創(chuàng)建一個切片,len為3议薪,cap為5
var i = make([]int, 0, 3) //創(chuàng)建一個切片尤蛮,len為0,cap為3
-
從數(shù)組中切取切片
數(shù)組和切片是緊密相連的斯议。切片可以用來訪問數(shù)組的部分或全部元素产捞,而這個數(shù)組稱為切片的底層數(shù)組。切片的指針指向數(shù)組第一個可以從切片中訪問的元素哼御,這個元素并不一定是數(shù)組的第一個元素坯临。
一個底層數(shù)組可以對應(yīng)多個切片,這些切片可以引用數(shù)組的任何位置恋昼,彼此之前的元素可以重疊看靠。
slice 操作符
s[i:j]
創(chuàng)建了一個新的 slice,這個新的 slice 引用了 s 中從 i 到 j-1 索引位置的所有元素液肌。如果表達(dá)式省略了 i挟炬,那么默認(rèn)是
s[0:j]
;如果省略了 j嗦哆,默認(rèn)是s[i:len(s)]
谤祖;
//示例來源:The Go Programming Language
//創(chuàng)建一個數(shù)組
months := [...]string{1:"January", /*...*/, 12: "December"}
Q2 := months[4:7]
summer := months[6:9]
fmt.Println(Q2) //["April" "May" "June"]
fmt.Println(summer) //["June" "July" "August"]
月份名稱字符串?dāng)?shù)組與其對應(yīng)的兩個元素重疊的 slice 圖示
注意:切片與原數(shù)組或切片共享底層空間,修改切片會影響原數(shù)組或切片
-
迭代切片
切片可以用 range 迭代老速,但是要注意:如果只用一個值接收 range泊脐,則得到的只是切片的下標(biāo),用兩個值接收 range烁峭,則得到的才是下標(biāo)和對應(yīng)的值容客。
//使用一個值接收range, 則得到的是切片的下標(biāo)
for i := range months {
fmt.Println(i) //返回下標(biāo) 0 1 ... 12
}
//使用兩個值接收range秕铛,則得到的是下標(biāo)和對應(yīng)的值
for i, v := range months {
fmt.Println(i, v) //返回下標(biāo)0 1 ... 12 和 值 "" "January" ... "December"
}
-
切片拷貝
使用
copy
內(nèi)置函數(shù)拷貝兩個切片時,會將源切片的數(shù)據(jù)逐個拷貝到目的切片指向的數(shù)組中缩挑,拷貝數(shù)量取兩個切片的最小值但两。例如長度為 10 的切片拷貝到長度為 5 的切片時,將拷貝 5 個元素供置。也就是說谨湘,拷貝過程中不會發(fā)生擴(kuò)容。
copy 函數(shù)有返回值芥丧,它返回實(shí)際上復(fù)制的元素個數(shù)紧阔,這個值就是兩個 slice 長度的較小值。
4. 切片擴(kuò)容-append 函數(shù)
追加元素
- 通過
append()
函數(shù)可以在切片的尾部追加 N 個元素
var a []int
a = append(a, 1) // 追加一個元素
a = append(a, 1, 2, 3) // 追加多個元素
a = append(a, []int{1, 2, 3}...) // 追加一個切片续担,注意追加切片時后面要加...
- 使用 append()函數(shù)也可以在切片頭部添加元素
a = append([]int{0}, a...) // 在開頭添加一個元素
a = append([]int{1, 2, 3}, a...) // 在開頭添加一個切片
注:從頭部添加元素會引起內(nèi)存的重分配擅耽,導(dǎo)致已有元素全部復(fù)制一次。因此從頭部添加元素的開銷要比從尾部添加元素大很多
- 通過 append()函數(shù)鏈?zhǔn)讲僮鲝闹虚g插入元素
a = append(a[:i], append([]int{x}, a[i:]...)...) //在第i個位置上插入x
a = append(a[:i], append([]int{1, 2, 3}, a[i:]...)...) //在第i個位置上插入切片
使用鏈?zhǔn)讲僮髟诓迦朐匚镉觯趦?nèi)層 append 函數(shù)中會創(chuàng)建一個臨式切片乖仇,然后將a[i:]
內(nèi)容復(fù)制到新創(chuàng)建的臨式切片中,再將臨式切片追加至a[:i]
中询兴。
-
通過 append()和 copy()函數(shù)組合從中間插入元素
使用這種方式可以避免創(chuàng)建過程中間的臨式切片乃沙,也可以做到從中間插入元素
//中間插入一個元素
a = append(a, 0) //切片擴(kuò)展一個空間
copy(a[i+1:], a[i:]) //a[i:]向后移動一個位置
a[i] = x //設(shè)置新添加的元素
//中間插入多個元素
a = append(a, x...) //為x切片擴(kuò)展足夠的空間
copy(a[i+len(x):], a[i:]) //a[i:]向后移動len(x)個位置
copy(a[i:], x) //復(fù)制新添加的切片
使用此方式雖然稍顯復(fù)雜,但是可以減少創(chuàng)建中間臨時切片的開銷诗舰。
刪除元素
很遺憾警儒,Go 語言中并沒有提供直接刪除指定位置元素的方式。不過根據(jù)切片的性質(zhì)眶根,我們可以通過巧妙的拼接切片來達(dá)到刪除指定數(shù)據(jù)的目的冷蚂。
a = []int{1, 2, 3}
//刪除尾部元素
a = a[:len(a) - 1] //刪除尾部一個元素
a = a[:len(a) - N] //刪除尾部N個元素
//刪除頭部元素
a = [1:] //刪除開頭1個元素
a = [N:] //刪除開頭N個元素
//刪除中間元素
a = append(a[:i], a[i+1:]...) //刪除中間一個元素
a = append(a[:i], a[i+N:]...) //刪除中間N個元素
slice 擴(kuò)容
很多人以為 slice 是可以自動擴(kuò)充的, 估計(jì)都是 append
函數(shù)誤導(dǎo)的。其實(shí) slice 并不會自己自動擴(kuò)充, 而是 append
數(shù)據(jù)時, 該函數(shù)如果發(fā)現(xiàn)超出了 cap 限制自動幫我們擴(kuò)的汛闸。
使用 append 向 slice 追加元素時蝙茶,如果 slice 空間不足,則會觸發(fā) slice 擴(kuò)容诸老,擴(kuò)容實(shí)際上是分配一塊更大的內(nèi)存隆夯,將原 slice 的數(shù)據(jù)拷貝進(jìn)新 slice,然后返回新 slice别伏,擴(kuò)容后再將數(shù)據(jù)追加進(jìn)去蹄衷。
例如,當(dāng)向一個容量為 5 且長度也為 5 的切片再次追加 1 個元素時厘肮,就會發(fā)生擴(kuò)容愧口,如下圖所示。(示例來源:Go專家編程)
擴(kuò)容操作只關(guān)心容量类茂,會把原 slice 的數(shù)據(jù)拷貝至新 slice 中耍属,追加數(shù)據(jù)由 append 在擴(kuò)容后完成托嚣。由上圖可見,擴(kuò)容后新 slice 的長度仍然是 5厚骗,但容量由 5 提到了 10示启,原 slice 的數(shù)據(jù)也都拷貝到了新的 slice 指向的數(shù)組中。
擴(kuò)容容量的選擇遵循以下基本規(guī)則
- 如果原 slice 的容量小于 1024领舰,則新 slice 的容量將擴(kuò)大為原來的 2 倍夫嗓;
- 如果原 slice 的容量大于 1024,則新的 slice 的容量將擴(kuò)大為原來的 1.25 倍冲秽;
5. Go 切片舍咖,Python 切片,都是切片锉桑,有什么不同排霉?
Go 有切片 slice 類型,Python 有列表和元組刨仑,這兩種語言都有切片操作郑诺。但是它們的切片操作是完全不同的夹姥。
- 最大的不同就是
- Python 的切片產(chǎn)生的是新的對象杉武,對新對象的成員的操作不影響舊對象;
- Go 的切片產(chǎn)生的是舊對象一部分的引用辙售,對其成員的操作會影響舊對象轻抱;
究其原因還是底層實(shí)現(xiàn)不同
- Go 的切片,底層是一個三元組旦部。指針指向一塊連續(xù)的內(nèi)存祈搜,長度是已有成員數(shù),容量是最大成員數(shù)士八。切片時容燕,一般并不會申請新的內(nèi)存,而是對原指針進(jìn)行移動婚度,然后和新的長度蘸秘、容量組成一個切片類型值返回。也就是說蝗茁,Go 的切片操作通常會和生成該切片的切片或數(shù)組共享內(nèi)存醋虏。
- Python 的切片,其實(shí)就是指針數(shù)組哮翘。對它進(jìn)行切片颈嚼,會創(chuàng)建新的數(shù)組。在 Python 的切片中饭寺,并沒有容量的概念叫挟。
這其實(shí)也體現(xiàn)了腳本語言和編譯語言的不同。雖然兩個語言都有類似的切片操作霞揉;但是 Python 主要目標(biāo)是方便;Go 主要目標(biāo)卻是快速适秩。
- 在使用中,Go 切片和 Python 切片也有很多不同
首先秽荞,Go 的切片,其成員是相同類型的抚官,Python 的列表則不限制類型。
兩種語言都有[a:b]這種切片操作凌节,意義也類似,但是 Go 的 a倍奢、b 兩個參數(shù)不能是負(fù)數(shù)朴上,Python 可以是負(fù)數(shù),此時就相當(dāng)于從末尾往前數(shù)卒煞。
兩種語言都有
[a:b:c]
這種切片操作痪宰,意義卻是完全不同的。Go 中的 c 表示的是容量畔裕;而 Python 的 c 表示的是步長衣撬。
6. 切片陷阱
-
無法做比較
和數(shù)組不同的是,slice 無法做比較扮饶,因此不能用==來測試兩個 slice 是否擁有相同的元素具练。標(biāo)準(zhǔn)庫里面提供了高度優(yōu)化的函數(shù)
bytes.Equal
來比較兩個字節(jié) slice。但是對于其它類型的 slice甜无,就必須要自己寫函數(shù)來比較扛点。slice 唯一允許的比較操作是和 nil 進(jìn)行比較,例如
if slice == nil {/*...*/}
-
空切片和 nil 切片
空切片和 nil 切片是不同的毫蚓。
- nil 切片中占键,切片的指針指向的是空地址,其長度和容量都為零元潘。nil 切片和 nil 相等畔乙。
- 空切片,切片的指針指向了一個地址翩概,但其長度和容量也為 0牲距,和 nil 不相等返咱,通常用來表示一個空的集合。
var s []int // s == nil
var s = nil // s == nil
var s = []int{nil} // s == nil
var s = []int{} // s != nil
s := make([]int,0) // s != nil
-
使用 range 進(jìn)行切片迭代
當(dāng)使用 range 進(jìn)行切片迭代時牍鞠,range 創(chuàng)建了每個元素的副本咖摹,而不是直接返回對該元素的引用。如果使用該值變量的地址作為每個元素的指針难述,就會造成錯誤萤晴。
func main() {
a := []int{1, 2, 3, 4, 5}
for i, v := range a {
fmt.Printf("Value: %d, v-addr: %X, Elem-addr: %X\n",
v, &v, &a[i])
}
}
# output
Value: 1, v-addr: C0000AA058, Elem-addr: C0000CC030
Value: 2, v-addr: C0000AA058, Elem-addr: C0000CC038
Value: 3, v-addr: C0000AA058, Elem-addr: C0000CC040
Value: 4, v-addr: C0000AA058, Elem-addr: C0000CC048
Value: 5, v-addr: C0000AA058, Elem-addr: C0000CC050
從結(jié)果中可以看出,使用 range 進(jìn)行迭代時胁后,v 的地址是始終不變的店读,它并不是切片中每個變量的實(shí)際地址攀芯。而是在使用 range 進(jìn)行遍歷時,將切片中每個元素都復(fù)制到了同一個變量 v 中殖演。如果錯誤的將 v 的地址當(dāng)作切邊元素的地址年鸳,將會引發(fā)錯誤。
-
切片擴(kuò)容引發(fā)的問題
正因?yàn)橛袛U(kuò)容機(jī)制朋鞍。所以我們無法保證原始的 slice 和用 append 后的結(jié)果 slice 指向同一個底層數(shù)組妥箕,也無法證明它們就指向不同的底層數(shù)組更舞。同樣,我們也無法假設(shè)舊 slice 上對元素的操作會或者不會影響新的 slice 元素宇葱。所以黍瞧,通常我們將 append 的調(diào)用結(jié)果再次賦給傳入 append 的 slice原杂。
內(nèi)置 append 函數(shù)在向切片追加元素時,如果切片存儲容量不足以存儲新元素年局,則會把當(dāng)前切片擴(kuò)容并產(chǎn)生一個新的切片。
append 函數(shù)每次追加元素都有可能觸發(fā)切片擴(kuò)容矢否,即有可能返回一個新的切片,這正是 append 函數(shù)聲明中返回值為切片的原因赖欣,使用時應(yīng)該總是接收該返回值验庙。
建議
使用 append 函數(shù)時,謹(jǐn)記 append 可能會產(chǎn)生新的切片云矫,并謹(jǐn)慎的處理返回值汗菜。
-
append 函數(shù)誤用
使用 append 函數(shù)時,需要考慮 append 返回的切片是否跟原切片共享底層的數(shù)組巡揍。下面這段程序片段菌瘪,來看看函數(shù)返回的結(jié)果。
//示例來源:Go專家編程
func AppendDemo() {
x := make([]int, 0, 10)
x = append(x, 1, 2, 3)
y := append(x, 4)
z := append(x, 5)
fmt.Println(x)
fmt.Println(y)
fmt.Println(z)
}
//output
[1 2 3]
[1 2 3 5]
[1 2 3 5]
題目首先創(chuàng)建了一個長度為 0糜工,容量為 10 的切片 x录淡,然后向切片 x 追加了 1,2刨裆,3 三個元素彬檀。其底層的數(shù)組結(jié)構(gòu)如下圖所示
創(chuàng)建切片 y 為切片 x 追加一個元素 4 后窍帝,底層數(shù)組結(jié)構(gòu)如下圖所示
需要注意的是切片 x 仍然沒有變化,切片 x 中記錄的長度仍為 3慈俯。繼續(xù)向 x 追加元素 5 后,底層數(shù)組結(jié)構(gòu)如下圖所示
至此卖子,答案已經(jīng)非常明確了洋闽。當(dāng)向 x 繼續(xù)追加元素 5 后突梦,切片 y 的最后一個元素被覆蓋掉了。
此時切片 x 仍然為[1 2 3]刊懈,而切片 y 和 z 則為[1 2 3 5]娃闲。
建議
一般情況下,使用 append 函數(shù)追加新的元素時皇帮,都會用原切片變量接收返回值來獲得更新
a = append(a, elems...)
-
函數(shù)傳參
Go 語言中將切片作為函數(shù)參數(shù)傳遞會有什么神奇的現(xiàn)象,一起來看看下面這個示例将谊。
package main
import "fmt"
func main(){
a := []int{1, 2, 3} //長度為3渐白,容量為3
b := make([]int, 1, 10) //長度為1,容量為10
test(a,b)
fmt.Println("main a =", a)
fmt.Println("main b =", b)
}
func test(a,b []int){
a = append(a, 4) //引發(fā)擴(kuò)容眠砾,此時返回的a是一個新的切片
b = append(b, 2) //沒有引發(fā)擴(kuò)容托酸,仍然是原切片
a[0] = 3 //改變a切片元素
b[0] = 3 //改變b切片元素
fmt.Println("test a =", a) //打印函數(shù)內(nèi)的a切片
fmt.Println("test b =", b) //打印函數(shù)內(nèi)的b切片
}
//output
test a = [3 2 3 4]
test b = [3 2]
main a = [1 2 3]
main b = [3]
首先励堡,我們創(chuàng)建了兩個切片堡掏,a 切片長度和容量均為 3,b 切片長度為 1鹅龄,容量為 10。將 a 切片和 b 切片作為函數(shù)參數(shù)傳入 test 函數(shù)中迎卤。
在 test 函數(shù)中玷坠,對 a 切片和 b 切片做了如下兩點(diǎn)改動
分別使用 append 函數(shù)在 a 切片和 b 切片中追加一個元素
分別對 a 切片和 b 切片的第一個元素做了修改
分別在主函數(shù)中和 test 函數(shù)中輸出兩個切片,會發(fā)現(xiàn)在主函數(shù)中和 test 函數(shù)中兩個切片好像改了樟凄,又好像沒改兄渺,下面我們就來分析一下。
理論分析
當(dāng)我們將一個切片作為函數(shù)參數(shù)傳遞給函數(shù)的時候,采用的是值傳遞凳兵,因此我們傳遞給函數(shù)的參數(shù)其實(shí)是上面這個切片三元組的值拷貝。當(dāng)我們對切片結(jié)構(gòu)中的指針進(jìn)行值拷貝的時候饭望,得到的指針還是指向了同一個底層數(shù)組形庭。因此我們通過指針對底層數(shù)組的值進(jìn)行修改,從而修改了切片的值斟珊。
但是富纸,當(dāng)我們以值傳遞的方式傳遞上面的結(jié)構(gòu)體的時候,同時也是傳遞了len
和cap
的值拷貝堵漱,因?yàn)檫@兩個成員并不是指針涣仿,因此示惊,當(dāng)我們從函數(shù)返回的時候米罚,外層切片結(jié)構(gòu)體的len
和cap
這兩個成員并沒有改變丈探。
所以當(dāng)我們傳遞切片給函數(shù)的時候,并且在被調(diào)函數(shù)中通過append
操作向切片中增加了值糊肠,但是當(dāng)函數(shù)返回的時候遗锣,我們看到的切片的值還是沒有發(fā)生變化,其實(shí)底層數(shù)組的值是已經(jīng)改變了的(如果沒有觸發(fā)擴(kuò)容的話)弧圆,但是由于長度len
沒有發(fā)生改變搔预,所以我們看到的切片的值也沒有發(fā)生改變叶组。
題目再分析
有了前面的理論基礎(chǔ),我們再來分析一下 a船庇,b 切片的返回結(jié)果侣监。
-
a 切片作為參數(shù)傳至 test 函數(shù)中,在 test 中向 a 切片追加一個元素后窃爷,此時觸發(fā)擴(kuò)容機(jī)制姓蜂,返回的切片已經(jīng)不再是原切片,而是一個新的切片覆糟。后續(xù)對 a 切片中的第一個元素進(jìn)行修改也是對新切片進(jìn)行修改,對老切片不會產(chǎn)生任何影響。
所以,最終在主函數(shù)中 a 切片仍然為[1 2 3]漓藕,而在 test 函數(shù)中 a 切片變成了[3 2 3 4]挟裂。
b 切片作為參數(shù)傳至 test 函數(shù)中,在 test 中向 b 切片追加一個元素后栗竖,不會觸發(fā)擴(kuò)容機(jī)制渠啤,返回的仍然是原切片,所以在后續(xù)對 b 切片的修改都是在原切片中進(jìn)行的修改份名。故在 test 函數(shù)中 b 切片為[3 2]妓美。但是在主函數(shù)中確為[3],可以看出在 test 中對切片進(jìn)行修改確實(shí)反應(yīng)到主函數(shù)中了辰如,但是由于其 len 和 cap 沒有改變贵试,len 仍為 1,所以最終就只輸出切片中的第一個元素[3]呕童,但其底層數(shù)組的值其實(shí)已經(jīng)改變了夺饲。
本文由 GOLANG ROADMAP 發(fā)布施符!