panic

A: = make (int [], 3, 4) a [0] = 1 a [1] = 2 a [2] [3] = 4 = 3 aCopy the code

Although the capacity of A is 4, since the length of slice specified at the initial initialization is 3, a[3] will go out of bounds and panic will occur

panic: runtime error: index out of range [4] with length 3
Copy the code

So what does this 4 do?

structure

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}
Copy the code
  • The bottom layer of slice is an array, so there is a pointer to the bottom array
  • Slice has its own length, len
  • Slice has its own capacity cap

Slice capacity

Cap is used for capacity expansion. If you expect your slice to cram a lot of data into it as the application runs, you can make the cap larger to avoid frequent memory requests during capacity expansion.

Nonsense not to say, directly on the source code

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
        newcap = cap
} else {
        if old.len < 1024 {
                newcap = doublecap
        } else {
                // Check 0 < newcap to detect overflow
                // and prevent an infinite loop.
                for 0 < newcap && newcap < cap {
                        newcap += newcap / 4
                }
                // Set newcap to the requested cap when
                // the newcap calculation overflowed.
                if newcap <= 0 {
                        newcap = cap
                }
        }
}
Copy the code

This is the core code for Slice expansion, and a good command of it will blow the interviewer away. This is the code for go1.15, there may be differences in older versions. By reading the source code, you can summarize:

  1. If the new capacity is twice as large as the old capacity, the new capacity is used
S: = make s = (int [], 3, 3) append (s, 4, 7) FMT. Println (cap (s)) / / 8Copy the code

Initialize cap=3, append 4 elements in a row, need new capacity =7, but cap*2=6, so according to the code should be 7, but the test found 8. And that’s because go has subsequent logic that says if it’s odd, then it’s going to add 1.

2. If 1 is not sufficient, if the number of old elements is less than 1024, then the new capacity is twice the original capacity

S: = make s = (int [], 3, 3) append (s, 4) FMT. Println (cap (s)) / / 6Copy the code

At this time, only one capacity is expanded. Theoretically, the new capacity needs to be at least 4, less than 2* CAP =6, so the result is 6.

  1. If 1 is not satisfied and the number of old elements is greater than or equal to 1024, the old cap keeps multiplying by 1.25 until it is larger than the new capacity
s:=make([]int,1024)
add:=make([]int,570)
s = append(s,add...)
fmt.Println(cap(s))//1696
Copy the code

1280*1.25=1600>1594, cap=1600? However, the fact is 1696. Through the background debug, it is found that 1600 is indeed calculated from the above nuclear code, but it is not finished yet.

case et.size == sys.PtrSize:
    lenmem = uintptr(old.len) * sys.PtrSize
    newlenmem = uintptr(cap) * sys.PtrSize
    capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
    overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
    newcap = int(capmem / sys.PtrSize)
Copy the code

This code is going to be 1696.

Make with the new

  • Make can only be used with slice, map, and chan to return the object itself
  • The definition of a new parameter is a data type, which can be an int, string, struct, map, or slice. Instead of returning the object itself, it returns a pointer to the object
func new(Type) *Type
Copy the code

SAO operation:

aa:=new([]int)
*aa = append(*aa,1)
fmt.Println((*aa)[0]) // 1
Copy the code

Let’s just say it’s either a genius or an idiot

Empty slice versus nil slice

I have to say that Go is really magical, but let me give you an idea

  1. Nil slice: Len =0 cap=0; pointer=0
  2. Len =0 cap=0 pointer! = 0

When will nil and empty slice be created

nil empty
var a []int a:=make([]int,0)
a:=*new([]int) a:=[]int{}

Official advice: Use nil slice

var a1 []int a2:=*new([]int) a3:=make([]int,0) a4:=[]int{} fmt.Println(*(*[3]int)(unsafe.Pointer(&a1))) //[0 0 0] fmt.Println(*(*[3]int)(unsafe.Pointer(&a2))) //[0 0 0] fmt.Println(*(*[3]int)(unsafe.Pointer(&a3))) //[824634109648 0 0]  fmt.Println(*(*[3]int)(unsafe.Pointer(&a4))) //[824634109648 0 0]Copy the code

The address of the empty slice is the same, pointing to an address that has no space

var zerobase uintptr
if size == 0 {
      return unsafe.Pointer(&zerobase)
}
Copy the code