slice 三個屬性
golang 的slice是一個指向底層的數(shù)組的指針結(jié)構(gòu)體寸谜。 這個結(jié)構(gòu)體有三個屬性竟稳,1.指向數(shù)組指針,2.len: slice中元素的數(shù)量 3.cap:slice占用內(nèi)存數(shù)量程帕。 只有深刻理解這三個屬性才能在使用slice中不至于犯錯住练。
正確理解變量和共享
多個slice之間可以共享底層的數(shù)據(jù),并且引用的數(shù)組部分區(qū)間可能重疊
以上是golang 圣經(jīng)中的一句話愁拭。深刻理解這句話對于日程編程非常有意義讲逛。
1.什么時候共享數(shù)據(jù)會被其他變量修改
func f1() {
a1 := []int{1,2,3,4,5,6}
a2 := a1
a3 := a1[1:3]
a1[1] = 999
fmt.Println("a1=",a1,"a2=",a2,"a3=",a3)
}
運行結(jié)果
a1= [1 999 3 4 5 6] a2= [1 999 3 4 5 6] a3= [999 3]
Process finished with exit code 0
我們清楚的看到了數(shù)據(jù)共享,此時修改了a1 岭埠,兩位兩個變量都被修改
什么時候不會修改
func f2() {
a1 := []int{1,2,3,4,5,6}
a2 := a1
a3 := a1[1:3]
a2 = append(a2,888)
a1[1] = 999
fmt.Println("a1=",a1,"a2=",a2,"a3=",a3)
}
運行結(jié)果
a1= [1 999 3 4 5 6] a2= [1 2 3 4 5 6 888] a3= [999 3]
Process finished with exit code 0
可以雖然a1被修改盏混,a2并沒有修改。我們知道append函數(shù)會面臨內(nèi)存的重新分配惜论。所以等a2進行append的時候许赃,會重新申請內(nèi)存空間,將原有數(shù)組拷貝然后增加如新值馆类。也就是當append操作的時候混聊,此時a2 不在和a1 共享內(nèi)存了。所以后續(xù)對a1的操作是不會影響到a2.
3.所有的append操作都會隔斷內(nèi)存共享乾巧?
func f3() {
a1 := []int{1,2,3,4,5,6}
a2 := a1
a3 := a1[1:3]
a2 = append(a2,888)
a3 = append(a3,777)
a1[1] = 999
fmt.Println("a1=",a1,"a2=",a2,"a3=",a3)
}
運行結(jié)果
a1= [1 999 3 777 5 6] a2= [1 2 3 4 5 6 888] a3= [999 3 777]
Process finished with exit code 0
這次a3 是對a1進行切片操作賦值的新變量句喜。此時對a3進行append操作预愤,我們發(fā)現(xiàn)a1的值同步被修改了。所以此時a3 和a1 仍然是共享內(nèi)存咳胃,append并沒有申請新的內(nèi)存空間而是繼續(xù)在a3的數(shù)據(jù)末尾寫入植康,這樣對于a1 是覆蓋了原有值。
問題本質(zhì)是
a1= [1 999 3 777 5 6] cap(a1) = 6 a2= [1 2 3 4 5 6 888] cap(a2) = 12 a3= [999 3 777] cap(a3) = 5
重新運行后將三個變量cap值打印為以上輸出展懈。
問題的本質(zhì)是len 和cap 的值销睁。 在slice中,當len小于cap 的值的時候存崖, 進行append 操作是不會造成內(nèi)存的重新分配冻记。a3 是從a1切片操作而來,我們看到a3 初始化的len =2 金句,cap =5.所以在append中不會引起內(nèi)存重新分配檩赢,go 運行時會繼續(xù)將數(shù)據(jù)依次寫入。這樣就修改了a3 和a1共享的內(nèi)存空間违寞。 對于a2贞瞒,在初始化的時候len =cap =6. 在append操作的時候就會重新申請空間,go會分配當前空間 * 2 的內(nèi)存趁曼。所以append后的cap就是12 如上军浆。
總結(jié)
在對slice 復(fù)制的時候,如果面臨多個變量同時指向一個數(shù)組的時候挡闰,一定要考慮到數(shù)據(jù)的共享和內(nèi)存的重新分配乒融。