GO slicing strength steps on the pit

Overview

The slice of the GO language has been used for the past two days, and it can support cutting the middle part of the array. But in today’s use, there is a bug. After checking for a long time, I found that it was a slice problem. Simply write a demo to reproduce the situation at that time:

package main
import "fmt"
func main() {
 a := []int{1, 2, 3, 4, 5}
 b := a[2:4]
 b[0] = 9
 fmt.Println(a)
}

What do you think the output is? Here, see the result:

[1 2 9 4 5]

Confused?? What’s going on here?

(How can I always step on the pit in a language, stupid X)

solve confusion

Looking at the output of this GO code, when we modify bthe value of the first element of the aarray, the third element of the array is modified. Is there any connection between the two? Look carefully, bwhen the array is being cut, isn’t that what is being cut? aIs it the third and fourth element of the array? In this way, does it b[0]not correspond a[2]exactly ?

Bold assumption: ** GO does not cut an array out of a new array, but still uses the original array, just modifies the first address and length of the array. **

verify:

package main
import "fmt"
func main() {
 a := []int{1, 2, 3, 4, 5}
 b := a[2:4]
 b[0] = 9
 fmt.Printf("%p\n", &a[2])
 fmt.Printf("%p\n", &b[0])
}

The printed addresses are exactly the same, which confirms the previous conjecture. It is indeed an array. At the same time, modifying athe array will also affect the barray.

Is it possible to baccess the array out of bounds and access athe value of the array? No, GO will check the array out of bounds.

After viewing the documentation, I found that the internal implementation of GO slices contains three fields. The meaning of each field is as follows:

  1. Array first address pointer: Points to the first address of the underlying array (this is the real array)
  2. Array length: the currently used length of the array
  3. Array capacity: The total length of the allocated memory of the array, the part that is more than the length of the array , occupies the memory and has not been used.

From this point of view, cutting it does not copy the whole, and it is very friendly to the operation of large slices. After all, the underlying array is shared, and only a small amount of data needs to be created. Just pay attention to the synchronous modification of the array. .

In this way, it seems to explain why it is called slice. **Slicing is to cut out part of the underlying array without creating a new array. **

The slice has capacity, so what is bthe capacity of the slice above? I looked at it: the length is 2, and the capacity is 3.

When the capacity of GO’s slice is sufficient, it will not dynamically expand. (The expansion will create a new array with a larger capacity and copy the original array data). That is to say, if I bappend data to it, it can affect the original array. the back data??

Try it out:

package main
import "fmt"
func main() {
 a := []int{1, 2, 3, 4, 5}
 b := a[2:4]
 b = append(b, 10)
 fmt.Println(b)
 fmt.Println(a)
}

Sure enough, the append operation still uses the original array if capacity allows.

So: the capacity of the slice is actually the capacity of the underlying array

At the same time, with the previous understanding of GO, I know that all functions of GO are performed by passing copy values, and the same is true for passing slices, and the structure of slices contains (array pointer, length, capacity) three elements, the underlying array It does not belong to the value itself, so the cost of copying the slice passed between functions is very small, and the modification of the slice by the function will also be reflected on the underlying array. Similarly, if the expansion operation is performed on the slice in the function, the change It will not affect the original data, because the new array is operated after the expansion.

OK. The slicing ends here. Simply put, there is another layer on top of the array. The slices of the slice share the underlying array.

Finally, the way GO creates arrays and slices (arrays and slices are different data structures):

// 方括号为空, 创建的是切片类型
a := []int{1, 2}
// 方括号指定长度, 创建的是真正的数组类型
b := [2]int{}

Summarize

So far, I have a new understanding of GO’s slices. When using slices, you need to pay special attention. The interception of slices shares the underlying array with the original object, and special attention should be paid to data modification.

If you need a safe and modifiable slice, you can use a copyfunction to copy a brand new array out of it, separate from the original array.

Leave a Reply

Your email address will not be published.

en_USEnglish