func main(){ s1:=make([]int,0,10) s1=[]int{1,2,3} ss:=make([]int,0,10) ss = s1[1:] for i:=0;i<len(ss);i++{ ss[i] +=10 } fmt.Println(s1) // [1 12 13] ss =append(ss,4) for i:=0;i<len(ss);i++{ ss[i] +=10 } fmt.Println(s1) // [1 12 13] 而不是 [1,22,23] t:=[]int{0} printPoint(t) // 0xc4200140a8 cap(s)= 1 t = append(t,1) printPoint(t) // 0xc4200140c0 0xc4200140c8 cap(s)= 2 t = append(t,2) printPoint(t) // 0xc4200160e0 0xc4200160e8 0xc4200160f0 cap(s)= 4 t = append(t,3) printPoint(t) // 0xc4200160e0 0xc4200160e8 0xc4200160f0 0xc4200160f8 cap(s)= 4 } func printPoint(s []int){ for i:=0;i<len(s);i++{ fmt.Print(unsafe.Pointer(&s[i])," ") } fmt.Println("cap(s)=",cap(s)) }
發現slice在進行append操做時會跟據原來的slice容量,若是append完成後新slice的容量超過原來slice的容量,則須要擴容,而且將舊的slice數據所有遷移到新的slice開闢的地址裏。數組
type slice struct { array unsafe.Pointer len int cap int } // growslice handles slice growth during append. // It is passed the slice element type, the old slice, and the desired new minimum capacity, // and it returns a new slice with at least that capacity, with the old data // copied into it. // The new slice's length is set to the old slice's length, // NOT to the new requested capacity. // This is for codegen convenience. The old slice's length is used immediately // to calculate where to write new values during an append. // TODO: When the old backend is gone, reconsider this decision. // The SSA backend might prefer the new length or to return only ptr/cap and save stack space. // 與append(slice,s)對應的函數growslice // 經過切片的類型,舊切片的容量和數據得出新切片的容量,新切片跟據容量從新申請一塊地址,把舊切片的數據拷貝到新切片中 func growslice(et *_type, old slice, cap int) slice { // 單純地擴容,不寫數據 if et.size == 0 { if cap < old.cap { panic(errorString("growslice: cap out of range")) } // append should not create a slice with nil pointer but non-zero len. // We assume that append doesn't need to preserve old.array in this case. return slice{unsafe.Pointer(&zerobase), old.len, cap} } // 擴容規則 1.新的容量大於舊的2倍,直接擴容至新的容量 // 2.新的容量不大於舊的2倍,當舊的長度小於1024時,擴容至舊的2倍,不然擴容至舊的5/4倍 newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap } else { if old.len < 1024 { newcap = doublecap } else { for newcap < cap { newcap += newcap / 4 } } } // 跟據切片類型和容量計算要分配內存的大小 var lenmem, newlenmem, capmem uintptr const ptrSize = unsafe.Sizeof((*byte)(nil)) switch et.size { case 1: lenmem = uintptr(old.len) newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) newcap = int(capmem) case ptrSize: lenmem = uintptr(old.len) * ptrSize newlenmem = uintptr(cap) * ptrSize capmem = roundupsize(uintptr(newcap) * ptrSize) newcap = int(capmem / ptrSize) default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size capmem = roundupsize(uintptr(newcap) * et.size) newcap = int(capmem / et.size) } // 異常狀況,舊的容量比新的容量還大或者新的容量超過限制了 if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) { panic(errorString("growslice: cap out of range")) } var p unsafe.Pointer if et.kind&kindNoPointers != 0 { // 爲新的切片開闢容量爲capmem的地址空間 p = mallocgc(capmem, nil, false) // 將舊切片的數據搬到新切片開闢的地址中 memmove(p, old.array, lenmem) // The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length). // Only clear the part that will not be overwritten. // 清理下新切片中剩餘地址,不能存放堆棧指針 // memclrNoHeapPointers clears n bytes starting at ptr. // // Usually you should use typedmemclr. memclrNoHeapPointers should be // used only when the caller knows that *ptr contains no heap pointers // because either: // // 1. *ptr is initialized memory and its type is pointer-free. // // 2. *ptr is uninitialized memory (e.g., memory that's being reused // for a new allocation) and hence contains only "junk". memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. p = mallocgc(capmem, et, true) if !writeBarrier.enabled { memmove(p, old.array, lenmem) } else { for i := uintptr(0); i < lenmem; i += et.size { typedmemmove(et, add(p, i), add(old.array, i)) } } } return slice{p, old.len, newcap} }
func main(){ s:=make([]int,0,5) s=append(s,1,2,3,4) printPoint(s) // 1 0xc420018120 2 0xc420018128 3 0xc420018130 4 0xc420018138 cap(s)= 5 &s= 0xc42000a060 processSlice(s) //11 0xc420018120 12 0xc420018128 13 0xc420018130 14 0xc420018138 11 0xc420018140 cap(s)= 5 &s= 0xc42000a080 } func processSlice(ss []int){ for i:=0;i<len(ss);i++{ ss[i] +=10 } ss=append(ss,11) printPoint(ss) } func printPoint(s []int){ for i:=0;i<len(s);i++{ fmt.Print(s[i],unsafe.Pointer(&s[i])," ") } fmt.Println("cap(s)=",cap(s),"&s=",unsafe.Pointer(&s)) }
函數中的形參slice是實參的拷貝,指向切片的指針不一樣,因爲sice沒有擴容,函數裏面的slice和主函數的實參slice指向的數組地址是同樣的app
func main(){ s:=make([]int,0,4) s=append(s,1,2,3,4) printPoint(s) // 1 0xc42008c000 2 0xc42008c008 3 0xc42008c010 4 0xc42008c018 cap(s)= 4 &s= 0xc42008a020 processSlice(s) // 11 0xc420092000 12 0xc420092008 13 0xc420092010 14 0xc420092018 11 0xc420092020 cap(s)= 8 &s= 0xc42008a040 } func processSlice(ss []int){ for i:=0;i<len(ss);i++{ ss[i] +=10 } ss=append(ss,11) printPoint(ss) } func printPoint(s []int){ for i:=0;i<len(s);i++{ fmt.Print(s[i],unsafe.Pointer(&s[i])," ") } fmt.Println("cap(s)=",cap(s),"&s=",unsafe.Pointer(&s)) }
函數中的形參slice是實參的拷貝,指向切片的指針不一樣,因爲sice擴容了,函數裏面的slice和主函數的實參slice指向的數組地址是不同的ide