Home > Backend Development > Golang > Go Slices and Subslices: Understanding Shared Memory and Avoiding `append()` Pitfalls

Go Slices and Subslices: Understanding Shared Memory and Avoiding `append()` Pitfalls

Barbara Streisand
Release: 2025-01-29 00:21:10
Original
881 people have browsed it

Go Slices and Subslices: Understanding Shared Memory and Avoiding `append()` Pitfalls

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() What is the slice in the GO language?

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>
Copy after login
Copy after login
Copy after login

The length of the slicing (the number of elements it contains)

    Capacity (the number of elements it can contain before the need to increase)
  1. This is where things become interesting. If you have multiple slices that are derived from the same array, changes made by a slice will be reflected in other slices because they share the same underlying array. len
  2. Let's see the following example:
  3. cap
  4. Understand slicing capacity

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>
Copy after login
Copy after login
of the new sub -slicing is determined by the remaining capacity of the original slice from the sub -section. Let's break down a little:

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.

Get sub -slicing

When you get the sub -slicing from the existing slices:

  • The length is the number of elements you specified.
  • Capacity Calculated the capacity of the original slice minus the starting index of sub -slices.
Let's see a detailed example:

<code class="language-go">type slice struct {
    array unsafe.Pointer // 指向底层数组的指针
    len   int           // 切片中的元素数量
    cap   int           // 底层数组的容量
}</code>
Copy after login
Copy after login
Copy after login
    The original slice has 5 elements, and the length and capacity are 5.
  • When you use , it points to the elements from index 1 to 3 (2, 3, 4).
  • subslice := original[1:4] The
  • length
  • is 4-1 = 3. subslice The capacity
  • is 5-1 = 4, because it starts from index 1 and includes elements to the end of the original slice.
  • subslice Trap!
This is where developers are often trapped. The function in Go language may cause unexpected behaviors when processing sub -sections.

Unused capacity sharing append() The

capacity

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()

Let's consider this example:

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>
Copy after login
Copy after login
both reflect these changes because they share the same underlying array.
  • subslice Are you surprised?
  • The operation modified the original slice because there is enough capacity in the underlying array. However, if we exceed the scope of capacity or additional elements exceeding the capacity permit, Go will allocate a new array for sub -sections to break the sharing with the original slicing:
  • subslice
  • In this case,
  • created a new underlying array because the original capacity has exceeded. 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>
Copy after login

append()

The main advantage is:

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.
  • Use the complete slice expression
<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>
Copy after login

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:

    i. Data protection: The original data remains unchanged, prevent accidents and side effects
  • II. Predicate behavior: The effect of the function on the input does not hide

    III. Parallel security: During the processing process, the original data can be used safely in other goroutine

    Remember:

      Slice is a reference to the underlying array
    • Sub -slicing and parental sharing memory
    • Whether the creation of a new underlying array depends on the capacity
    • append() When adding elements of sub -slicing elements with capacity, it will modify the data of the parent slicing.
    • When you want to avoid sharing, please use explicit memory management
    • When processing sub -slices, please perform any of the following operations:
    I wish you a happy code. Remember, the greater the ability, the greater the responsibility, especially in the sharing of memory! ?
    <code class="language-go">type slice struct {
        array unsafe.Pointer // 指向底层数组的指针
        len   int           // 切片中的元素数量
        cap   int           // 底层数组的容量
    }</code>
    Copy after login
    Copy after login
    Copy after login

    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!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template