What is the slice

Slice is an “array” that can be added, deleted, modified, queried, traversed, and automatically expanded, just like ArrayList in Java. Slice is not concurrently safe.

This article is based on Go 1.14. If you have any questions or suggestions during the process, please leave a message to me in the comment section or the public account. We can communicate and learn together

The article has some assembly knowledge, can first learn “go language advanced programming” – assembly chapter, can not find to my public number (in the end) message “GO language advanced programming” to get

Slice memory structure and search ideas

Train of thought

func main(a) {
    a := make([]int.0)
    a = append(a, 1)
    fmt.Println(a)
}
Copy the code

Use the go tool compile-s -l -n main.go to find the underlying function

"".main STEXT size=328 args=0x0 locals=0x98.0x0042 00066 (main.go:6)        CALL    runtime.makeslice(SB)
        ...
        0x007c 00124 (main.go:7)        CALL    runtime.growslice(SB)
        ...
Copy the code

The specific code is as follows

// This simply expresses the array address at the bottom of slice
func makeslice(et *_type, len.cap int) unsafe.Pointer{... }// This reflects the structure of slice
func growslice(et *_type, old slice, cap int) slice{...return slice{p, old.len, newcap}
}
Copy the code

The main idea is to create the “return” of the function and the “parameter” or “return” of the function during the operation, so we can see the specific structure as follows

Memory structure

type slice struct {
    array unsafe.Pointer     // Address of the underlying array
    len   int                / / slice len
    cap   int                / / slice of cap
}
Copy the code

Question 1:var a []int[]int{}What’s the difference?

func main(a) {
    var a []int
    fmt.Println(a == nil) //true
    b := []int{}
    fmt.Println(b == nil) //false
}
Copy the code

First let’s look at how slices are created

Create a way The code example
Direct statement var a []int
make a := make([]int, len)

a := make([]int, len, cap)
Literally said a := []int{}
The interception a = a[i:j]

a = a[i:j:k]
New (Thanks for Lao Qian’s article, the original address is at the end of the article) a := *new([]int)

Direct statement

func main(a) {
    var a []int
    fmt.Println(a)        / / []
    fmt.Println(len(a))   / / 0
    fmt.Println(cap(a))   / / 0
    fmt.Println(a == nil) //true
}
Copy the code

make

Make ([]int, len)

func main(a) {
    a := make([]int.1) // If make has only two arguments, len and cap both have the value of the second argument
    fmt.Println(a)      / / [0]
    fmt.Println(len(a)) / / 1
    fmt.Println(cap(a)) / / 1
}
Copy the code

Make ([]int, len, cap)

func main(a) {
    a := make([]int.1.2) If make has three arguments, len is the value of the second argument and cap is the value of the third argument
    fmt.Println(a)         / / [0]
    fmt.Println(len(a))    / / 1
    fmt.Println(cap(a))    / / 2
}
Copy the code

Go: compile -S -l -N main. Go: compile -S -l -N main

"".main STEXT size=557 args=0x0 locals=0xe0.0x002f 00047 (main.go:6)        PCDATA  $0, $1
        0x002f 00047 (main.go:6)        PCDATA  $1, $0
        0x002f 00047 (main.go:6)        LEAQ    type.int(SB), AX
        0x0036 00054 (main.go:6)        PCDATA  $0, $0
        0x0036 00054 (main.go:6)        MOVQ    AX, (SP)
        0x003a 00058 (main.go:6)        MOVQ    $1.8(SP)
        0x0043 00067 (main.go:6)        MOVQ    $2.16(SP)
        0x004c 00076 (main.go:6)        CALL    runtime.makeslice(SB) // Create []int from here
        0x0051 00081 (main.go:6)        PCDATA  $0, $1
        0x0051 00081 (main.go:6)        MOVQ    24(SP), AX
        0x0056 00086 (main.go:6)        PCDATA  $1, $1
        0x0056 00086 (main.go:6)        MOVQ    AX, "".a+120(SP)
        0x005b 00091 (main.go:6)        MOVQ    $1."".a+128(SP)
        0x0067 00103 (main.go:6)        MOVQ    $2."".a+136(SP)
        ...
Copy the code

We query makeslice to find the corresponding compilation logic

/ / position is/CMD/compile/internal/gc/typecheck. Go
func typecheck1(n *Node, top int) (res *Node){... ok :=0
    switch n.Op {
    ...

    case OMAKE: // check the make syntax
        ok |= ctxExpr
        args := n.List.Slice() // make([]int, 1) \ make([]int, 1, 2); make([]int, 1, 2)
        if len(args) == 0 {
            yyerror("missing argument to make")
            n.Type = nil
            return n
        }

        n.List.Set(nil)
        l := args[0]
        l = typecheck(l, ctxType) // where l is []int
        t := l.Type
        if t == nil {
            n.Type = nil
            return n
        }

        i := 1
        switch t.Etype {
            ...
        case TSLICE:
            if i >= len(args) {
                yyerror("missing len argument to make(%v)", t)
                n.Type = nil
                return n
            }
            
            l = args[i] //len
            i++         / / 2
            l = typecheck(l, ctxExpr)
            var r *Node
            if i < len(args) { // if make has 3 arguments, r is cap, otherwise nil
                r = args[i]
                i++
                r = typecheck(r, ctxExpr)
            }
            
            ... / / check
            
            ifIsconst(l, CTINT) && r ! =nil && Isconst(r, CTINT) && l.Val().U.(*Mpint).Cmp(r.Val().U.(*Mpint)) > 0 { // If len > cap, an error occurs
                yyerror("len larger than cap in make(%v)", t)
                n.Type = nil
                return n
            }
            
            n.Left = l
            n.Right = r
            
            /* In syntax. Go, const is const (... OMAKESLICE // make(Type, Left, Right) (type is slice) ... ) */ = */; */ = */n.Op = OMAKESLICE ... }... }... }Copy the code

Next we jump to the OMAKESLICE stage

/ / position is CMD/compile/internal/gc/walk. Go
func walkexpr(n *Node, init *Nodes) *Node {

opswitch:
    switch n.Op {
        ...
    case OMAKESLICE:
        l := n.Left   // len
        r := n.Right  // cap
        if r == nil { // add no cap to len
            r = safeexpr(l, init)
            l = r
        }
        t := n.Type

        if n.Esc == EscNone { // EscNone, like map before, does not escape to the heap
            // If it does not escape to the heap, the bottom layer is an array, and then intercepts cap. }else{...len.cap := l, r

            fnname := "makeslice64"
            argtype := types.Types[TINT64]

            if (len.Type.IsKind(TIDEAL) || maxintval[len.Type.Etype].Cmp(maxintval[TUINT]) <= 0) &&
                (cap.Type.IsKind(TIDEAL) || maxintval[cap.Type.Etype].Cmp(maxintval[TUINT]) <= 0) {
                fnname = "makeslice"
                argtype = types.Types[TINT]
            }

            ...

            fn := syslook(fnname) Makeslice64 / makeslice. }... }... }Copy the code

So there are two of them, as follows

func makeslice(typ *byte.len int.cap int) unsafe.Pointer
func makeslice64(typ *byte.len int64.cap int64) unsafe.Pointer
Copy the code

Makeslice64, which essentially performs makeslice as well

func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
    // Check len and cap
    len: =int(len64)
    if int64(len) != len64 {
        panicmakeslicelen()
    }

    cap: =int(cap64)
    if int64(cap) != cap64 {
        panicmakeslicecap()
    }

    return makeslice(et, len.cap)}Copy the code
// Here we see a Pointer returned (unsafe.Pointer), which represents the Pointer to the underlying array, the array of slice
// Determine whether len and CAP are out of memory or exceed the allocated capacity
func makeslice(et *_type, len.cap int) unsafe.Pointer { 
    // Since mallocGC's size is 0, the pointer to Zerobase is returned when mem is 0, so
    Et. size = 0 or cap = 0
    mem, overflow := math.MulUintptr(et.size, uintptr(cap)) 
    if overflow || mem > maxAlloc || len < 0 || len > cap {
        mem, overflow := math.MulUintptr(et.size, uintptr(len))
        if overflow || mem > maxAlloc || len < 0 { 
            panicmakeslicelen()
        }
        panicmakeslicecap()
    }

    return mallocgc(mem, et, true)}func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer{...if size == 0 { // Return a pointer to zerobase when size is 0
        return unsafe.Pointer(&zerobase)
    }
    ...
}

Copy the code

For mallocGC where size is 0, look at the following code

func main(a) {
    e := make([]int.0)
    fmt.Println(e == nil) //false
    e1 := *(*[3]int)(unsafe.Pointer(&e))
    fmt.Println(e1[0]) // 824634227792: indicates an array
    fmt.Println(e1[1]) // 0 indicates len
    fmt.Println(e1[2]) // 0, cap

    fmt.Println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -")

    // ** this is just for proof
    f := make([]struct{}, 10)
    fmt.Println(f == nil) //false
    f1 := *(*[3]int)(unsafe.Pointer(&f))
    fmt.Println(f1[0]) // 824634227792: indicates an array
    fmt.Println(f1[1]) // 10, for len
    fmt.Println(f1[2]) // 10, cap
}
Copy the code

In the above code, since cap is 0 and the size of _type in F is 0, the values of array in e are the same, which are both the addresses of Zerobase

*[3]int (*[3]int

Literally said

func main(a) {
    a := []int{1.2.3.5: 100}
    fmt.Println(a)      / /,2,3,0,0,100 [1]
    fmt.Println(len(a)) / / 6
    fmt.Println(cap(a)) / / 6

    b := []int{}
    fmt.Println(b)        / / []
    fmt.Println(len(b))   / / 0
    fmt.Println(cap(b))   / / 0
    fmt.Println(b == nil) // false
}
Copy the code

As you can see here, we can initialize it by indexing the value directly

The interception

Select a portion of an array or slice to intercept, not exceeding the index range of the underlying array

Mode 1: ARR [I: J]

func main(a) {
    a := make([]int.5.10)
    for i := 0; i < len(a); i++ {
        a[i] = i
    }
    i := 1
    j := 3
    b := a[i:j]         // The length index range is [1:3], but cap is cap(a) -i
    fmt.Println(b)      / / [1, 2]
    fmt.Println(len(b)) // 2, this is the length of b, which is j minus I
    fmt.Println(cap(b)) Cap (a) -i = cap(a) -i
}
Copy the code

Mode 2: ARR [I :j: K]

func main(a) {
    a := make([]int.5.10)
    for i := 0; i < len(a); i++ {
        a[i] = i
    }
    i := 1
    j := 3
    k := 5
    b := a[i:j:k]       // The length index range is [1:3], but cap is k-i
    fmt.Println(b)      / / [1, 2]
    fmt.Println(len(b)) // 2, this is the length of b, which is j minus I
    fmt.Println(cap(b)) // 4, this is the capacity of B, k minus I
}
Copy the code

Here is a diagram for not exceeding the bounds of the underlying array

func main(a) {
    a := make([]int.5.10)
    fmt.Println(a)      / /,0,0,0,0 [0]
    fmt.Println(len(a)) / / 5
    fmt.Println(cap(a)) / / 10

    b := a[2:7]         // This has exceeded len of A
    fmt.Println(b)      / /,0,0,0,0 [0]
    fmt.Println(len(b)) / / 5
    fmt.Println(cap(b)) / / 8

    // The following is beyond the range of choices beyond the range of a cap
    //c := a[3:11]
    //fmt.Println(c)

    d := a[1:2:5]
    fmt.Println(d)      / / [0]
    fmt.Println(len(d)) / / 1
    fmt.Println(cap(d)) / / 4

    //e := a[4:7:11]
    //fmt.Println(e)
    //fmt.Println(len(e))
    //fmt.Println(cap(e))

    f := b[2:8]         // This also exceeds len of B
    fmt.Println(f)      / /,0,0,0,0,0 [0]
    fmt.Println(len(f)) / / 6
    fmt.Println(cap(f)) / / 6

    //g := b[2:9]
    //fmt.Println(g)
    //fmt.Println(len(g))
    //fmt.Println(cap(g))
}
Copy the code

Details are shown below

Notice that the f and g are taken from the slice of B, but from the f and G, we know that the boundary here is not in terms of B, but in terms of len and cap of the underlying a

new

func main(a) {
    a := *new([]int)
    fmt.Println(a)      / / []
    fmt.Println(len(a)) / / 0
    fmt.Println(cap(a)) / / 0
}
Copy the code

Empty slices and nil slices

The above 4 methods are not different in daily use, but the specific underlying data is different. Here again, thanks to Old Money (at the end of the article), the concept of “empty slice” and “nil slice” is introduced

As an extension, runtime.convTslice is called in assembly when fmt.println (slice) is executed in code, as follows

func convTslice(val []byte) (x unsafe.Pointer) {
    if (*slice)(unsafe.Pointer(&val)).array == nil { / / logo
        x = unsafe.Pointer(&zeroVal[0])}else {
        x = mallocgc(unsafe.Sizeof(val), sliceType, true) * (* []byte)(x) = val
    }
    return
}
Copy the code

In the above code, we can convert a slice object that is essentially a []type (the “identity” section), as you can see in the following code

Nil pointer looks like this

func main(a) {
    var a []int
    fmt.Println(a == nil) //true

    []int is not used here because the slice structure is known to have only three fields
    //a1 := *(*[]int)(unsafe.Pointer(&a))
    a1 := *(*[3]int)(unsafe.Pointer(&a))
    fmt.Println(a1[0]) // 0: array
    fmt.Println(a1[1]) // 0 indicates len
    fmt.Println(a1[2]) // 0, cap

    fmt.Println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -")

    b := *new([]int)
    fmt.Println(b == nil) //true
    b1 := *(*[3]int)(unsafe.Pointer(&b))
    fmt.Println(b1[0]) // 0: array
    fmt.Println(b1[1]) // 0 indicates len
    fmt.Println(b1[2]) // 0, cap
}
Copy the code

We can see that both a and B have array values of 0

The null pointer looks like this

func main(a) {
    c := []int{}
    fmt.Println(c == nil) //false
    c1 := *(*[3]int)(unsafe.Pointer(&c))
    fmt.Println(c1[0]) // 824634227792: indicates an array
    fmt.Println(c1[1]) // 0 indicates len
    fmt.Println(c1[2]) // 0, cap

    fmt.Println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -")

    e := make([]int.0)
    fmt.Println(e == nil) //false
    e1 := *(*[3]int)(unsafe.Pointer(&e))
    fmt.Println(e1[0]) // 824634227792: indicates an array
    fmt.Println(e1[1]) // 0 indicates len
    fmt.Println(e1[2]) // 0, cap
}
Copy the code

It can be seen that the array corresponding to the bottom layer of C and E is a fixed value. From the above “create mode – make”, we can know the address of Zerobase

A total of the following

Create a way Slice type
Direct statement Nil slice
make Empty section
new Nil slice
[]int{} Empty section

How to choose

Case 1:

When judging the JSON format, the following situations occur, depending on the requirements of the scene

Func main() {s := Out{} var a []int s.tmp = a byt, _ := json.Marshal(s) fmt.Println(string(byt)) //{"Tmp":null} b := make([]int, 0) s.Tmp = b byt, _ = json.Marshal(s) fmt.Println(string(byt)) //{"Tmp":[]} }Copy the code

Case 2: For other daily use, nil slicing is officially recommended. For details, see github.com/golang/go/w…

Problem 2: Does append of child slice affect parent slice

Scenario 1: WHEN B slices A and appends it, why does it affect A

func main(a) {
    a := make([]int.1.10)
    fmt.Println("before:", a) // before : [0]
    add(a)
    fmt.Println("after:", a) // After: [10] // Here is a change
}

func add(src []int) {
    src = append(src, 1)
    src[0] = 10
    fmt.Println("add:", src) / / do: [10, 1]
}
Copy the code

Scenario 2: Using append affects the old part as described in scenario 1. Why does it not affect the old part now?

func main(a) {
    a := make([]int.1)
    fmt.Println("before:", a) // before : [0]
    add(a)
    fmt.Println("after:", a)  // After: [0
}

func add(src []int) {
    src = append(src, 1)
    src[0] = 10
    fmt.Println("add:", src) / / do: [10, 1]
}
Copy the code

Let’s look at the following

Append usage mode

Append to the end of the slice. If the underlying array runs out of space, a new slice is created and the data is copied over (explained below).

Method 1: Append (Slice, ELEm)

func main(a) {
    a := make([]int.1)
    fmt.Println(a)      / / [0]
    fmt.Println(len(a)) / / 1
    fmt.Println(cap(a)) / / 1

    a = append(a, 2)
    fmt.Println(a)      / / [0, 2]
    fmt.Println(len(a)) / / 2
    fmt.Println(cap(a)) / / 2
}
Copy the code

Method 2: Append (Slice, slice2…)

func main(a) {
    a := []int{1.2.3.4.5.6}
    var b []int
    // Even though there are three arguments in mode 2, after append, we still want len, not cap
    b = append(b, a[1:3:5]...).// 
    fmt.Println(b)             / / [2, 3]
    fmt.Println(len(b))        / / 2
    fmt.Println(cap(b))        / / 2
}
Copy the code

Problem resolution

Scenario 1 is shown in the figure below. Although B adds it, the underlying array of A and B is the same, so B’s operation affects A’s data

So why doesn’t scenario 2 have an impact? Let’s look at the add function in assembly language

"".add STEXT size=440 args=0x18 locals=0x98.0x002f 00047 (main.go:13)       MOVQ    "".src+160(SP), DX // DX indicates the array of SRC
        0x0037 00055 (main.go:13)       MOVQ    "".src+168(SP), BX // BX stands for len of SRC
        0x003f 00063 (main.go:13)       PCDATA  $1, $1
        0x003f 00063 (main.go:13)       MOVQ    "".src+176(SP), SI // SI indicates SRC cap
        0x0047 00071 (main.go:13)       LEAQ    1(BX), DI // Di=BX+1, len+1
        0x004b 00075 (main.go:13)       CMPQ    DI, SI // This compares Len +1 with cap
        0x004e 00078 (main.go:13)       JLS     85 // If len+1 is not higher than cap, jump to 85
        0x0050 00080 (main.go:13)       JMP     349 // Jump to 349.0x015d 00349 (main.go:13)       PCDATA  $0, $1
        0x015d 00349 (main.go:13)       MOVQ    BX, ""..autotmp_5+64(SP)
        0x0162 00354 (main.go:13)       PCDATA  $0, $5
        0x0162 00354 (main.go:13)       LEAQ    type.int(SB), AX // Get the address of type int
        0x0169 00361 (main.go:13)       PCDATA  $0, $1
        0x0169 00361 (main.go:13)       MOVQ    AX, (SP)   // Indicates the type of slice
        0x016d 00365 (main.go:13)       PCDATA  $0, $0
        0x016d 00365 (main.go:13)       MOVQ    DX, 8(SP)  // Indicates an array of old slice
        0x0172 00370 (main.go:13)       MOVQ    BX, 16(SP) // Len of old slice
        0x0177 00375 (main.go:13)       MOVQ    SI, 24(SP) // Cap for old slice
        0x017c 00380 (main.go:13)       MOVQ    DI, 32(SP) // Indicates the cap corresponding to the parameter
        0x0181 00385 (main.go:13)       CALL    runtime.growslice(SB) // 这里表示新建一个 slice
        0x0186 00390 (main.go:13)       PCDATA  $0, $1
        0x0186 00390 (main.go:13)       MOVQ    40(SP), DX // This represents the array of the new slice
        0x018b 00395 (main.go:13)       MOVQ    48(SP), AX // Len of new slice
        0x0190 00400 (main.go:13)       MOVQ    56(SP), SI // This represents the cap of new slice.Copy the code

As you can see in “Line 9 of assembly code”, a new slice is created in scenario 2 because len >= cap, so the underlying array address of the slice is inconsistent with the original one, so the underlying array of the old slice will not be affected. Specific growslice related parts are shown below

func growslice(et *_type, old slice, cap int) slice{...var p unsafe.Pointer
    // The new address has been obtained
    if et.ptrdata == 0 {
        p = mallocgc(capmem, nil.false)... }else {
        p = mallocgc(capmem, et, true)... } memmove(p, old.array, lenmem)// The old array data is migrated here

    return slice{p, old.len, newcap} // Then return a new slice
}
Copy the code

If a new slice is not created, it will have an impact, as shown in the following code

func main(a) {
    a := make([]int.1.2)
    fmt.Println("before:", a) // before : [0]
    add(a)
    fmt.Println("after:", a) // After: [10] // Here the original is affected
}

func add(src []int) {
    src = append(src, 1)
    src[0] = 10
    fmt.Println("do:", src) / / do: [10, 1]
}
Copy the code

Question 3: Does Append have any special treatment for nil slices

The following code, generally for go and nil are panic processing, so what is the magic power of the bottom?

func main(a) {
    var a []int
    fmt.Println(a == nil) //true
    a = append(a, 1)
    fmt.Println(a) / / [1]
}
Copy the code

Problem resolution

Go by compiling go tool compile-s -l -n main.go, see

"".main STEXT size=152 args=0x0 locals=0x60.0x0030 00048 (main.go:5)        LEAQ    type.int(SB), AX
        0x0037 00055 (main.go:5)        PCDATA  $0, $0
        0x0037 00055 (main.go:5)        MOVQ    AX, (SP) // this represents *_type
        0x003b 00059 (main.go:5)        XORPS   X0, X0
        0x003e 00062 (main.go:5)        MOVUPS  X0, 8(SP)  // old.array
        0x0043 00067 (main.go:5)        MOVQ    $0.24(SP) // old.cap
        0x004c 00076 (main.go:5)        MOVQ    $1.32(SP) Cap = 1; cap = 1
        0x0055 00085 (main.go:5)        CALL    runtime.growslice(SB) // See above
        0x005a 00090 (main.go:5)        PCDATA  $0, $1
        0x005a 00090 (main.go:5)        MOVQ    40(SP), AX // This represents the array of the newly generated slice
        0x005f 00095 (main.go:5)        MOVQ    48(SP), CX // This represents len of the newly generated slice
        0x0064 00100 (main.go:5)        MOVQ    56(SP), DX // This represents the cap of the newly generated slice
        0x0069 00105 (main.go:5)        INCQ    CX            // This represents the newly generated len + 1
        0x006c 00108 (main.go:5)        JMP     110
        0x006e 00110 (main.go:5)        MOVQ    $1, (AX)   // Set the position of the new array [0] to 1.Copy the code

Func growslice(et *_type, old slice, cap int) slice will create a slice with cap 1 inside. You can see the note above. The key point is in “line 9”.

Q4: What is the rule of slice expansion, multiplying by 2 each time?

As shown in the following code, the cap is multiplied by 2 each time the append number is increased. Is this true?

func main(a) {
    a := make([]int.0)
    a = append(a, 1)
    fmt.Println(len(a)) / / 1
    fmt.Println(cap(a)) / / 1

    a = append(a, 1)
    fmt.Println(len(a)) / / 2
    fmt.Println(cap(a)) / / 2

    a = append(a, 1)
    fmt.Println(len(a)) / / 3
    fmt.Println(cap(a)) / / 4
}
Copy the code

Let’s take a look at the growslice source code as a whole

Growslice source code parsing

func growslice(et *_type, old slice, cap int) slice{...if cap < old.cap {
        panic(errorString("growslice: cap out of range"))}// et.size Indicates the type size of the slice
    if et.size == 0 { Struct {} type = 0
        return slice{unsafe.Pointer(&zerobase), old.len.cap}
    }

    newcap := old.cap // 老的 slice 的cap
    doublecap := newcap + newcap // Cap *2 for old Slice
    if cap > doublecap {// If the cap is greater than oldCap *2, the final cap is the desired cap
        newcap = cap
    } else {
        if old.len < 1024 { // If len of the old slice is less than 1024, the final cap is the old cap*2
            newcap = doublecap
        } else {
            for 0 < newcap && newcap < cap { // If newCap is less than the required cap, the newcap is increased by a factor of 5/4 until the cap is exceeded
                newcap += newcap / 4
            }
            if newcap <= 0 {// If the value of newcap overflows, the final cap is cap
                newcap = cap}}}...var p unsafe.Pointer
    // The new address has been obtained
    if et.ptrdata == 0 {
        p = mallocgc(capmem, nil.false)... }else {
        p = mallocgc(capmem, et, true)... } memmove(p, old.array, lenmem)// The old array data is migrated here

    return slice{p, old.len, newcap} // Then return a new slice
}
Copy the code

Regular summary

When len of old Slice is less than 1024

Question 5: What is the difference between slice and array in Golang?

The code is sloppy, just get what you want

func main(a) {
    a := []int{0.0}
    fmt.Println(a)      / / [0, 0]
    fmt.Println(len(a)) / / 0
    fmt.Println(cap(a)) / / 0
    doSlice(a)
    fmt.Println(a) / / (1, 0]

    fmt.Println("-- -- -- -- --")

    b := [2]int{}
    fmt.Println(b)      / / [0, 0]
    fmt.Println(len(b)) / / 2
    fmt.Println(cap(b)) / / 2
    doArray(b)
    fmt.Println(b) / / [0, 0]

    a = append(a, 1)
    //b = append(b, 1) // Array cannot use append
}

func doArray(src [2]int) {
    src[0] = 1
}

func doSlice(src []int) {
    src[0] = 1
}
Copy the code

Different points:

  • Len and Cap of arrays are fixed and consistent, while len of slices <= cap, and both change with append

  • Arrays do not affect the original data. Slice does not affect the original data without scaling

  • Arrays cannot be filled with append, whereas slice can

Slices can also be obtained by array interception, but be careful not to exceed the index range of the underlying array

Reference materials (in no particular order)

Deep Decryption of The Go Language slice – Rao Quancheng

An In-depth Analysis of the Three Special States of “Slice” in Go – Old Money Big Guy

Golang Compilation

Golang Assembly Conditional Jump Command

Assembly instruction Interpretation

— — — — — — — — — — — — — — — — — — — — — — — — — — — — this is the line — — — — — — — — — — — — — — — — — — — — — — — —

The following is my public number, welcome to flirt