In -depth understanding of GO language slicing: shared memory and
trap append()
Hi everyone! Welcome to my blog. ? If you are here, you may have just come into contact with Golang, or you are an experienced developer, and you want to understand the internal working principle of the section. So let's start!
GO language is highly praised because of its simplicity and efficiency -as people often say, "the GO language is to complete the work." For developers from C, C or Java and other languages, the simple grammar and ease of use of Go language are refreshing. However, even in the Go language, some characteristics may confuse developers, especially when processing slices and sub -slices. Let's unveil these subtleties and better understand how to avoid common traps of
and sliced to share memory.
append()
Usually, when you need a data structure to store a series of values, slicing is the first choice in Go language. Their flexibility comes from such a fact: their length is not part of its type. This feature overcomes the restrictions of the array, enabling us to create a single function that can handle any slices and enables the slice to increase or expand according to needs. Although the slice has some similarities with the array, such as both indexing and length, they have different data management methods. Slice acts as a reference to the underlying array, which actually stores the data of slices. In essence, the slice provides a view of some or all elements of the array. Therefore, when you create a slice, Go will automatically process the bottom array of creating sliced elements/data.
The shared memory of sliced
The array is a continuous memory block, but what makes the slice interesting is how they quote this memory. Let's break down the structure of the slice:
When you create a slice, it contains three components:
Poems pointing to the underlying array
<code class="language-go">type slice struct { array unsafe.Pointer // 指向底层数组的指针 len int // 切片中的元素数量 cap int // 底层数组的容量 }</code>
The length of the slicing (the number of elements it contains)
len
cap
Before we further deepen, let's try to understand the slicing capacity
. When you get sub -slices from the existing Go language slicing, the
capacity<code class="language-go">package main import "fmt" func main() { // 创建一个具有初始值的切片 original := []int{1, 2, 3, 4, 5} // 创建一个子切片——两个切片共享相同的底层数组! subslice := original[1:3] fmt.Println("未修改的子切片:", subslice) // 输出 => 未修改的子切片: [2 3] // 修改子切片 subslice[0] = 42 fmt.Println("原始切片:", original) // 输出 => 原始切片: [1 42 3 4 5] fmt.Println("修改后的子切片:", subslice) // 输出 => 修改后的子切片: [42 3] }</code>
When you create slices from arrays, the length of the slice is the number of elements it originally contained, and its capacity is the total number of elements that it can contain before it needs to grow.
When you get the sub -slicing from the existing slices:
<code class="language-go">type slice struct { array unsafe.Pointer // 指向底层数组的指针 len int // 切片中的元素数量 cap int // 底层数组的容量 }</code>
subslice := original[1:4]
The subslice
The capacity subslice
Trap!
Unused capacity sharing append()
The
of the sub -slicing includes elements that do not belong to their length but located in the original slicing capacity range. This means that if sub -slices increase, it can access or modify these elements. append()
Initially pointing to 2, 3, the capacity is 4 (it can grow to the end of the original slice).
When you add 60, 70 to , it uses the surplus capacity of the original slice.and
<code class="language-go">package main import "fmt" func main() { // 创建一个具有初始值的切片 original := []int{1, 2, 3, 4, 5} // 创建一个子切片——两个切片共享相同的底层数组! subslice := original[1:3] fmt.Println("未修改的子切片:", subslice) // 输出 => 未修改的子切片: [2 3] // 修改子切片 subslice[0] = 42 fmt.Println("原始切片:", original) // 输出 => 原始切片: [1 42 3 4 5] fmt.Println("修改后的子切片:", subslice) // 输出 => 修改后的子切片: [42 3] }</code>
subslice
Are you surprised? subslice
original
subslice
The best practice of avoiding traps
append()
Clarify the capacity
<code class="language-go">func main() { // 原始切片 original := []int{1, 2, 3, 4, 5} // 创建一个子切片 subslice := original[1:4] // 指向元素 2, 3, 4 fmt.Println("子切片:", subslice) // 输出 => 子切片: [2 3 4] fmt.Println("子切片的长度:", len(subslice)) // 输出 => 子切片的长度: 3 fmt.Println("子切片的容量:", cap(subslice)) // 输出 => 子切片的容量: 4 }</code>
append()
i. Create a new slice with its own independent underlying array. This is important -it is not just a new section head, but a brand new array in the memory.
II. Then only transmit the value instead of memory reference. This is like a copy of a file instead of sharing the original file.<code class="language-go">func main() { original := []int{1, 2, 3, 4, 5} subslice := original[1:3] // 指向元素 2, 3 fmt.Println("追加前原始切片:", original) // 输出 => [1 2 3 4 5] fmt.Println("追加前子切片:", subslice) // 输出 => [2 3] fmt.Println("子切片的容量:", cap(subslice)) // 输出 => 4 // 在容量范围内追加到子切片 subslice = append(subslice, 60, 70) // 追加到子切片后打印 fmt.Println("追加后原始切片:", original) // 输出 => [1 2 3 60 70] fmt.Println("追加后子切片:", subslice) // 输出 => [2 3 60 70] }</code>
When passing the slice to a function that should not modify the original data, consider non -variability
make([]int, len(subslice))
copy()
The main advantage is:
III. Parallel security: During the processing process, the original data can be used safely in other goroutine
Remember:
append()
When adding elements of sub -slicing elements with capacity, it will modify the data of the parent slicing. <code class="language-go">type slice struct { array unsafe.Pointer // 指向底层数组的指针 len int // 切片中的元素数量 cap int // 底层数组的容量 }</code>
Congratulations on reading this article.
Do you think this resource is helpful? Do you have any questions? Or did you find an error or typo? Please leave your feedback in the comments. Don't forget to share this resource with others who may benefit from it. Follow me to get more information.
The above is the detailed content of Go Slices and Subslices: Understanding Shared Memory and Avoiding `append()` Pitfalls. For more information, please follow other related articles on the PHP Chinese website!