ホームページ > バックエンド開発 > Golang > スライスとサブスライスに行く:共有されたメモリを理解し、 `append()`落とし穴を回避する

スライスとサブスライスに行く:共有されたメモリを理解し、 `append()`落とし穴を回避する

Barbara Streisand
リリース: 2025-01-29 00:21:10
オリジナル
881 人が閲覧しました

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

in -go言語スライシングの詳細な理解:共有メモリとトラップ

append()こんにちはみんな!私のブログへようこそ。あなたがここにいるなら、あなたはちょうどゴランと接触したかもしれません、またはあなたは経験豊富な開発者であり、あなたはセクションの内部作業原則を理解したいです。始めましょう!

GO言語は、そのシンプルさと効率性のために高く評価されています。 C、C、またはJavaおよびその他の言語の開発者にとって、GO言語の単純な文法と使いやすさはさわやかです。ただし、GO言語でも、特にスライスやサブスライスを処理する場合、いくつかの特性が開発者を混乱させる可能性があります。これらの微妙さを発表し、

の一般的なトラップを避け、メモリを共有するためにスライスする方法をよりよく理解しましょう。

GO言語のスライスは何ですか? append()

通常、一連の値を保存するためにデータ構造が必要な場合、GO言語でスライスすることが最初の選択です。それらの柔軟性は、そのような事実から来ています。それらの長さはそのタイプの一部ではありません。この機能は、配列の制限を克服し、スライスを処理し、ニーズに応じてスライスを増加または拡張できる単一の関数を作成できるようにします。

スライスには、インデックス作成と長さの両方など、アレイといくつかの類似点がありますが、データ管理方法は異なります。スライスは、実際にスライスのデータを保存する基礎となる配列への参照として機能します。本質的に、スライスは、配列の一部またはすべての要素のビューを提供します。したがって、スライスを作成するとき、GOはスライスした要素/データの作成の下部配列を自動的に処理します。

スライスされた共有メモリ

配列は連続的なメモリブロックですが、スライスを興味深いものにしているのは、このメモリを引用する方法です。スライスの構造を分解しましょう:

スライスを作成すると、3つのコンポーネントが含まれています。 基礎となる配列を指している

スライシングの長さ(それに含まれる要素の数)
<code class="language-go">type slice struct {
    array unsafe.Pointer // 指向底层数组的指针
    len   int           // 切片中的元素数量
    cap   int           // 底层数组的容量
}</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

容量(増加する必要がある前に含めることができる要素の数)

  1. これが物事が面白くなる場所です。同じ配列から派生した複数のスライスがある場合、スライスによって行われた変更は、同じ基礎となる配列を共有するため、他のスライスに反映されます。
  2. 次の例を見てみましょう:len
  3. スライシング容量を理解cap
さらに深くなる前に、スライス容量を理解してみましょう

。既存のGO言語スライシングからサブスライスを取得すると、新しいサブスライスの

容量

は、サブセクションからの元のスライスの残りの容量によって決定されます。少し分解しましょう:

アレイからスライスを作成する場合、スライスの長さは元々含まれていた要素の数であり、その容量は、成長する必要がある前に含めることができる要素の総数です。

sub -slicingを取得

既存のスライスからサブスライスを取得した場合:

  • 長さは、指定した要素の数です。
  • 容量元のスライスの容量を除いて、サブスライスの開始インデックスを引いたものに計算しました。
詳細な例を見てみましょう:

<code class="language-go">type slice struct {
    array unsafe.Pointer // 指向底层数组的指针
    len   int           // 切片中的元素数量
    cap   int           // 底层数组的容量
}</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
    元のスライスには5つの要素があり、長さと容量は5です。
  • を使用すると、インデックス1から3(2、3、4)の要素を指します。
  • subslice := original[1:4]
  • 長さ
  • は4-1 = 3です。 subslice 容量
  • は5-1 = 4です。これは、インデックス1から始まり、元のスライスの最後まで要素を含むためです。
  • subslice trap!
これは、開発者がしばしば閉じ込められる場所です。 GO言語の関数は、サブセクションを処理するときに予期しない動作を引き起こす可能性があります。

未使用の容量共有append() サブスライシングの容量

には、それらの長さに属さないが、元のスライシング容量範囲にある要素が含まれます。これは、サブスライスが増加すると、これらの要素にアクセスまたは変更できることを意味します。

append()この例を考えてみましょう:

最初に2、3を指している

容量は4です(元のスライスの端まで成長できます)。 60、70からを追加すると、元のスライスの余剰容量を使用します。

は、同じ基礎となる配列を共有するため、これらの変更を反映しています。
<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基礎となる配列に十分な容量があるため、操作は元のスライスを変更しました。ただし、容量の許可を超える容量の範囲または追加要素の追加要素を超える場合、GOはサブセクションの新しい配列を割り当てて、元のスライスとの共有を破壊します。
  • この場合、
  • は、元の容量が超えているため、新しい基礎となる配列を作成しました。 subslice
  • トラップを回避するベストプラクティス original容量を明確にしますsubslice

append()主な利点は次のとおりです

i。これは重要です - それは単なる新しいセクションヘッドではなく、メモリ内の新しい配列です。
<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>
ログイン後にコピー

ii。これは、元のファイルを共有する代わりに、ファイルのコピーのようなものです。 append()

完全なスライス式を使用します

  • を考慮してください。
<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>
ログイン後にコピー
主な利点は次のとおりです

iデータ保護:元のデータは変更されていません。

II:入力に対する関数の効果は非表示になります

iii:処理プロセス中、元のデータは他のゴルウチンで安全に使用できます

覚えておいてください:

    スライスは、基礎となる配列への参照です
  • サブスライシングと親の共有メモリ
  • 新しい基礎となる配列の作成が容量に依存するかどうか
  • append()サブスライス要素の要素を容量に追加すると、親のスライスのデータが変更されます。
  • 共有を避けたい場合は、明示的なメモリ管理を使用してください
  • 幸せなコードをお祈りします。覚えておいてください、特に記憶の共有において、能力が大きくなるほど、責任が大きくなります! ?
この記事を読んでおめでとうございます。
<code class="language-go">type slice struct {
    array unsafe.Pointer // 指向底层数组的指针
    len   int           // 切片中的元素数量
    cap   int           // 底层数组的容量
}</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

このリソースは役立つと思いますか?質問はありますか?それともエラーやタイプミスを見つけましたか?コメントにフィードバックを残してください。


このリソースを恩恵を受ける可能性のある他の人と共有することを忘れないでください。詳細については、私に従ってください。

以上がスライスとサブスライスに行く:共有されたメモリを理解し、 `append()`落とし穴を回避するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート