Time：2022-5-18

# Go slice full resolution

Directory structure:

array

section

• Bottom structure
• establish
• General statement
• Make mode
• intercept
• Boundary problem
• Extended expression
• Capacity expansion mechanism
• Slice transfer pit
• Copy of slice
• Shallow copy
• Deep copy

## array

``````var n [4]int
fmt. Println (n) // output: [0]
n[0] = 1
n[3] = 2
fmt. Println (len (n)) // output: 4
fmt. Println (cap (n)) // output: 4
fmt. Println (n) // output: [1 0 0 2]

b := n
n[0] = 2
fmt. Println (b) // output: [1 0 0 2]
b[0] = 3
fmt. Println (n) // output: [2 0 0 2]``````

explain:

• stay`var n [4]int`The initialization of the array has been completed, and all the values are assigned to 0, and the length and capacity are 4
• Assigning n to B is equivalent to assigning n`copy`Operation, and then`copy`The result is assigned to B, so n and B belong to two arrays respectively and do not affect each other

## section

### Bottom structure

``````type slice struct {
array unsafe. Pointer // pointer to the underlying array
Len int // slice length
Cap int // capacity of underlying array
}``````

### establish

#### Declaration method

The default value is nil, and the initial length and capacity are 0

``````var s []int
fmt.Println(cap(s)) // 0
fmt.Println(len(s)) // 0
fmt.Println(s == nil) // true``````

#### Make create

`make([]interface{}, len, cap)`

adopt`make`The default value is not nil, and the initial length and capacity can be specified, which is not disturbed by the automatic capacity expansion mechanism`len`When the parameter is not 0, it will be automatically assigned as an array

``````a := make([]int, 0, 10)
fmt.Println(len(a)) // 0
fmt.Println(cap(a)) // 10
fmt.Println(a == nil) // false
b := make([]int, 1000)
fmt.Println(len(b)) // 1000
fmt.Println(cap(b)) // 1000
c := make([]int, 5, 10)
fmt.Println(len(c)) // 5
fmt.Println(cap(c)) // 10
fmt.Println(c) // [0 0 0 0 0]``````

### intercept

Slices can be created based on arrays and slices, and the interception rule is to close left and open right

``````n := [5]int{1, 2, 3, 4, 5}
N1: = n [1:] // intercept from n array
fmt.Println(n1) // [2 3 4 5]
N2: = N1 [1:] // cut from N1 slice
fmt.Println(n2) // [3 4 5]``````

The slice and the original array or slice share the underlying space. Then, in the above example, modifying the element of N2 will affect the original slice and array:

``````N2 [1] = 6 // modifying N2 will affect the original slice and array
fmt.Println(n1) // [2 3 6 5]
fmt.Println(n2) // [3 6 5]
fmt.Println(n)  // [1 2 3 6 5]``````

#### Boundary problem

• 1. When n is an array or string expression`n[low:high]`Value relationship between low and high in:

0 <= low <=high <= len(n)

• 2. When n is slice, the expression`n[low:high]`The maximum value of high becomes cap (n), and the value relationship between low and high:

0 <= low <=high <= cap(n)

If the above conditions are not met, an out of bounds panic will be sent.

The difference is that the bounded array is`len(n)`, slice is`cap(n)`

Built in function`append()`Used to append elements to the slice.

``````n := make([]int, 0)
N = append (n, 1) // add an element
N = append (n, 2, 3, 4) // add multiple elements
n = append(n, []int{5, 6, 7}...) //  Add a slice
fmt.Println(n)                   // [1 2 3 4 5 6 7]``````

During the append operation, if the slice capacity is insufficient, the expansion will be triggered. Follow the example above:

``````fmt. Println (cap (n)) // capacity equals 8
n = append(n, []int{8, 9, 10}...)
fmt. Println (cap (n)) // the capacity is equal to 16, and the capacity has been expanded``````

When the capacity is 8 at the beginning and slices [] int {8, 9, 10} are added later, the capacity becomes 16.

If append exceeds the length of the slice, a new slice will be produced and the original one will not be overwritten:

``````N2: = n [1:4:5] // length equals 3 and capacity equals 4
fmt.Printf("%p\n", n2) // 0xc0000ac068
n2 = append(n2, 5)
fmt.Printf("%p\n", n2) // 0xc0000ac068
n2 = append(n2, 6)
fmt. Printf ("% P \ n", N2) // address changed, 0xc0000b8000``````

### Extended expression

The new slice produced by simple expression will share the underlying array with the original array or slice. Although copy is avoided, it will bring some risks. In the following example, when the new N1 slice append adds an element, it overwrites the value of index position 4 of the original n, which may cause your program to be unexpected, resulting in adverse consequences

``````n := []int{1, 2, 3, 4, 5, 6}
n1 := n[1:4]
fmt.Println(n)       // [1 2 3 4 5 6]
fmt.Println(n1)      // [2 3 4]
N1 = append (N1, 100) // change the value of index position 4 of N from 5 to 100
fmt.Println(n)       // [1 2 3 4 100 6]
fmt.Println(n1) // [2 3 4 100]
fmt.Println(len(n[1:4])) // 3
fmt.Println(cap(n[1:4])) // 5``````

The length of n [1:4] is 3. It’s easy to understand (4-1). Why is the capacity 5?

Because slice n [1:4] and slice n share the underlying spaceTherefore, its capacity is not equal to its length 3. According to the position where 1 is equal to index 1 (equal to value 2), there are five elements from value 2 to end element 6, so`n[1:4]`The capacity is 5.

Go 1.2[3] An expression that limits the capacity of new slices is provided in:

n[low:high:max]

Max represents the capacity of the newly generated slice, and the capacity of the new slice is equal to`max-low`, the relationship between low, high and Max in the expression:

0 <= low <= high <= max <= cap(n)

Continuing with the previous example, we will recalculate the capacity with the value of Max instead of sharing the capacity of N, but N2 and N still share the same underlying array

``````n2 := n[1:4:5]
fmt.Println(cap(n2)) // 4
fmt. Println (N2) // output [2 3 4]
n[3] = 111
fmt. Println (N2) // output [2 3 111]``````

### Capacity expansion mechanism

There are many online articles on the capacity expansion mechanism of go slice, and many conclusions are as follows:

Conclusion 1:

• 1. When the required capacity exceeds twice the capacity of the original slice, the required capacity is used as the new capacity.
• 2. When the length of the original slice is less than 1024, the capacity of the new slice will be doubled directly. When the capacity of the original slice is greater than or equal to 1024, it will be increased by 25% repeatedly until the new capacity exceeds the required capacity.

Conclusion 2:

• Based on the estimated capacity of slice 1`memory alignment `, after the capacity calculation, the efficient utilization of memory should be considered for memory alignment.
##### example
``````package main

func main() {
s := []int{1,2}
s = append(s, 3,4,5)
Println (cap (s)) // output 6
}``````

Due to initial`s`The capacity of is 2. Now three elements need to be added, so through`append`It will trigger capacity expansion and call`growslice`Function, whose input parameter`cap`The size is 2 + 3 = 5. By doubling the original capacity`doublecap` = 2+2，`doublecap`less than`cap`Value, so the expected capacity value calculated in the first stage`newcap=5`。 In the second stage, the element type size`int`and`sys.PtrSize`Equal, pass`roundupsize`Round up the size of memory to`capmem`=48 bytes, so the capacity of the new slice`newcap`48 / 8 = 6, successful interpretation!

In slice`append`During operation, if the underlying array has no space to accommodate additional elements, it needs to be expanded. Capacity expansion is not to increase memory space on the basis of the original underlying array, but to allocate a new memory space as the underlying array of slices, and copy the original data and additional data to the new memory space.

It is relatively complex to determine the capacity of expansionAnd CPU bits, element size, whether to include pointers, and the number of additionsAnd so on. After reading the expansion source code logic, we find it unnecessary to tangle with the exact expansion value.

In actual use, if the capacity range of the slice can be determined, it is more appropriate to allocate sufficient capacity space during slice initialization, and the performance loss caused by capacity expansion will not be considered during append operation.

### Slice transfer pit

#### Example 1

There are the following examples

``````func modifySlice(innerSlice []string) {
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Println(innerSlice)
}

func main() {
outerSlice := []string{"a", "a"}
modifySlice(outerSlice)
fmt.Print(outerSlice)
}

//The output is as follows
[b b]
[b b]``````

In the above example, the slice content has been modified.

#### Example 2

``````func modifySlice(innerSlice []string) {
innerSlice = append(innerSlice, "a")
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Println(innerSlice)
}

func main() {
outerSlice := []string{"a", "a"}
modifySlice(outerSlice)
fmt.Print(outerSlice)
}

//The output is as follows
[b b a]
[a a]``````

explain:

• In the modifyslice method, innerslice is a copy of outerslice, but it refers to the same underlying array. Therefore, in example 1, the slice contents have been modified.
• Innerslice is a slice with the same len and cap. When the append method occurs, the capacity expansion operation will be carried out. The capacity expansion operation will generate a new slice, which is to make a deep copy in the original array and expand the capacity.

Print the details of the code and look at the output again

``````func modifySlice(innerSlice []string) {
fmt.Println("begin modify")
innerSlice = append(innerSlice, "a")
fmt.Printf("%p, %v\n", innerSlice, &innerSlice[0])
fmt.Println("innerSlice  len:", len(innerSlice), "cap:", cap(innerSlice))
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Println(innerSlice)
fmt.Println("end modify")
}

func main() {
outerSlice := []string{"a", "a"}
fmt.Printf("%p, %v\n", outerSlice, &outerSlice[0])
fmt.Println("outerSlice  len:", len(outerSlice), "cap:", cap(outerSlice))
modifySlice(outerSlice)
fmt.Println("outerSlice  len:", len(outerSlice), "cap:", cap(outerSlice))
fmt.Printf("%p, %v\n", outerSlice, &outerSlice[0])
fmt.Print(outerSlice)
}
//Output
0xc0000464e0, 0xc0000464e0
outerSlice  len: 2 cap: 2
begin modify
Innerslice len: 3 cap: 4 // capacity change
[b b a]
end modify
outerSlice  len: 2 cap: 2
0xc0000464e0, 0xc0000464e0
[a a]``````

Proved our conjecture.

### Example 3

``````func modifySlice(innerSlice []string) {
innerSlice = append(innerSlice, "a")
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Println(innerSlice)
}

func main() {
outerSlice := make([]string, 0, 3)
outerSlice = append(outerSlice, "a", "a")
modifySlice(outerSlice)
fmt.Println(outerSlice)
}

//Output
[b b a]
[b b]``````

explain:

• The capacity of the initialization slice is 3, so in`innerSlice`The capacity expansion operation will not occur, but because it is value transfer,`innerSlice`just`outerSlice`When the append operation is performed, a copy of the array is also inserted into the same array and changed at the same time`innerSlice`Length, but`outerSlice`Length of（`len`Field) has not changed, so it still prints out [b]

Supplement the print details and do some processing

``````func modifySlice(innerSlice []string) {
innerSlice = append(innerSlice, "a")
innerSlice[0] = "b"
innerSlice[1] = "b"
For {// continuously print the memory address and value of outerslice
time.Sleep(time.Second / 10)
fmt.Printf("%p\n", innerSlice)
fmt.Println(innerSlice)
}
}

func main() {
Outerslice: = make ([] string, 0, 3) // initialize slices with capacity of 3 and length of 0
outerSlice = append(outerSlice, "a", "a")
fmt. Printf ("outerslice% P \ n", outerslice) // print the initial memory address of innerslice
Go modifyslice (outerslice) // execute modifyslice
time. Sleep (time. Second / 5) // wait for the end of modifyslice
fmt. Println (outerslice) // print the innerslice value again
fmt. Println ("outerslice", len (outerslice), cap (outerslice)) // print the length and capacity of innerslice
outerSlice = append(outerSlice, "b")
fmt. Println (outerslice) // / print the innerslice value again
fmt. Printf ("outerslice% P \ n", outerslice) // print the memory address of innerslice again
time. Sleep (time. Second) // wait for the output of the modify method
}
//Output
Outerslice 0xc00000c4c60 // initial memory address of outerslice
0xc00000c4c60 // memory address of innerslice
[b a] // value after modify
[b b]
outerSlice 2 3
[b b b]
Outerslice 0xc00000c4c60 // the memory address of outerslice has not changed
0xc00000c4c60 // memory address of innerslice的值没有发生改变
The value of [b] // innerslice is overwritten
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]
0xc0000c4c60
[b b b]``````

It can be explained that whenappend()During execution, the same array is still shared without capacity expansion, but because it is value transfer,`innerSlice`It’s a copy. What’s changed is the of the copy`len``outerSlice`of`len`There is no actual change, so the output value will be higher than`innerSlice`less

### Copy of slice

#### Shallow copy

adopt`=`Operator copy slice, this is a shallow copy.

``````func main() {
a := []int{1, 2, 3}
b := a
fmt.Println(unsafe.Pointer(&a))  // 0xc00000c030
fmt.Println(a, &a[0])            // [100 2 3] 0xc00001a078
fmt.Println(unsafe.Pointer(&b))  // 0xc00000c048
fmt.Println(b, &b[0])            // [100 2 3] 0xc00001a078
}``````

adopt`[:]`Copy the slice in the same way, which is also a shallow copy.

``````func main() {
a := []int{1, 2, 3}
b := a[:]
fmt.Println(unsafe.Pointer(&a)) *// 0xc0000a4018*
fmt.Println(a, &a[0])      *// [1 2 3] 0xc0000b4000*
fmt.Println(unsafe.Pointer(&b)) *// 0xc0000a4030*
fmt.Println(b, &b[0])      *// [1 2 3] 0xc0000b4000*
}``````

#### Deep copy

Deep copy, need to use`copy()`Built in function

`func copy(dst, src []Type) int`

Its return value represents the number of copied elements in the slice

``````func main() {
a := []int{1, 2, 3}
b := make([]int, len(a), len(a))
copy(b, a)
fmt.Println(unsafe.Pointer(&a)) *// 0xc00000c030*
fmt.Println(a, &a[0])      *// [1 2 3] 0xc00001a078*
fmt.Println(unsafe.Pointer(&b)) *// 0xc00000c048*
fmt.Println(b, &b[0])      *// [1 2 3] 0xc00001a090*
}``````

The number of copy elements is related to the size and capacity of the original slice and the target slice, and it only replaces the data from the original slice without generating a new slice

``````func main() {
a := []int{1, 2, 3}
b := []int{-1, -2, -3, -4}
c := []int{-1, -2}

fmt.Println(unsafe.Pointer(&b)) //0xc0000040f0
copy(b, a)
fmt.Println(unsafe.Pointer(&a)) // 0xc0000040d8
fmt.Println(a, &a[0])           // [1 2 3] 0xc0000145e8
fmt.Println(unsafe.Pointer(&b)) // 0xc0000040f0
fmt.Println(b, &b[0])           // [1 2 3 -4] 0xc0000101e0
fmt.Println(unsafe.Pointer(&c)) //0xc000004108
copy(c, a)
fmt.Println(unsafe.Pointer(&a)) // 0xc0000040d8
fmt.Println(a, &a[0])           // [1 2 3] 0xc0000145e8
fmt.Println(unsafe.Pointer(&c)) // 0xc000004108
fmt.Println(c, &c[0])           // [1 2] 0xc0000129a0
}``````