- 1. Do a little experiment first
Func Main () {s1:=make ([]int, 0,10) S1=[]int{A} SS:=make ([]int, 0,10) SS= S1[1:] forI:=0;i<len (ss); i++{Ss[i]+=10} fmt. Println (S1)//[1]SS =append (ss,4) forI:=0;i<len (ss); i++{Ss[i]+=10} fmt. Println (S1)//[1 12 13] Instead of [1,22,23]T:=[]int{0} printpoint (t)//0xc4200140a8 Cap (s) = 1t = Append (t,1) Printpoint (t)//0xc4200140c0 0xc4200140c8 Cap (s) = 2t = Append (t,2) Printpoint (t)//0xc4200160e0 0xc4200160e8 0xc4200160f0 cap (s) = 4t = Append (t,3) Printpoint (t)//0xc4200160e0 0xc4200160e8 0xc4200160f0 0xc4200160f8 cap (s) = 4}func Printpoint (s []int){ forI:=0;i<len (s); i++{fmt. Print (unsafe. Pointer (&s[i]), "")} FMT. Println ("Cap (s) =", Cap (s))}
found that slice in the append operation will be based on the original slice capacity, if the append after the completion of the new slice capacity than the original slice capacity, you need to expand, and the old slice data all migrated to the new slice opened the address.
- 2. Locate the slice.go in the runtime directory and locate the function growslice (et *_type, old slice, cap int)
type slice struct {array unsafe. Pointer LenintCapint}//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 this 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 was 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 was gone, reconsider this decision.//The SSA backend might prefer the new length or to return only Ptr/cap and save stack space.//functions corresponding to append (slice,s) GrowsliceBy the type of slice, the capacity of the old tile and the data to derive the capacity of the new slice, the new slice with the capacity to re-request a piece of address, the old slice of the data copied to the new slice func growslice (et *_type, oid slice, cap int) Slice {//to simply enlarge , do not write data 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}}//expansion rules 1. The new capacity is greater than the old twice times, directly to the new capacity//2. The new capacity is not more than the old twice times, when the old length is less than 1024, the expansion to the old twice times, otherwise expanded to the old 5/ 4 times times Newcap: = Old.cap Doublecap: = Newcap + newcap If cap > doublecap {newcap = cap} else { If Old.len < 1024x768 {Newcap = Doublecap} else {for Newcap < cap {NEWCA p + = NEWCAP/4}}}//the size to allocate memory according to the slice type and capacity 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) CA Se ptrsize:lenmem = uintptr (old.len) * ptrsize newlenmem = uintptr (CAP) * ptrsize Capmem = roundupsi Ze (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 (CAPM em/et.size)}//abnormal condition, older capacity is larger than new capacity or new capacity exceeds limit 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 {//For new slices open capacity for capmem address space p = MALLOCGC (Capmem, nil, false)//move data from old slices To the address opened by the new slice Memmove (p, Old.array, LENMEM)//The Append () that calls Growslice are going to overwrite from old . Len to cap (which'll be the new length). Only clear the PART that will is not overwritten.//clean up the remaining address in the new slice, cannot hold the stack pointer//memclrnoheappointers clears n bytes starting at ptr.////usually You should use TYPEDMEMCLR. Memclrnoheappointers should be//used only if 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 avoid s 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}}
- Do not easily append slices, if the new tile capacity than the old one, need to do growslice operation, new address opening, data copy
- Set the initial capacity value of the slice as much as possible to avoid growslice, like make ([]int,0,100)
- A slice is a structure that holds the capacity of the slice, the actual length, and the address of the array