Golang の「append()」が、チャネル経由で送信されたスライスを変更しているように見えるのはなぜですか?

Linda Hamilton
リリース: 2024-11-02 10:01:31
オリジナル
260 人が閲覧しました

Why Does Golang's `append()` Seem to Modify a Slice After It's Sent Over a Channel?

Golang append() は新しいスライスを作成するとき?

append() のドキュメントには、新しいスライスを割り当て、元のスライスの容量が足りない場合に要素をコピーします。ただし、ブール文字の組み合わせを生成する次のコードの出力を調べると、矛盾が生じます。

<code class="go">package main

import (
    "fmt"
)

func AddOption(c chan []bool, combo []bool, length int) {
    if length == 0 {
        fmt.Println(combo, "!")
        c <- combo
        return
    }
    var newCombo []bool
    for _, ch := range []bool{true, false} {
        newCombo = append(combo, ch)
        AddOption(c, newCombo, length-1)
    }
}

func main() {
    c := make(chan []bool)
    go func(c chan []bool) {
        defer close(c)
        AddOption(c, []bool{}, 4)
    }(c)
    for combination := range c {
        fmt.Println(combination)
    }
}</code>
ログイン後にコピー

Contrasting Observations

出力では、感嘆符で終わる行は、AddOption によってチャネル経由で送信されたスライスを表し、感嘆符のない行は main() で受信したスライスを示します。注目すべき点は、append() が新しいスライスを返したとされるにもかかわらず、チャネル経由で送信されたスライスが送信後に変更されているように見えることです。

ソースの検査

疑わしいコードブロック

<code class="go">var newCombo []bool
for _, ch := range []bool{true, false} {
    newCombo = append(combo, ch)
    AddOption(c, newCombo, length-1)
}</code>
ログイン後にコピー

ドキュメントによると、元のスライスの容量が十分でない場合、append() は新しい基礎となるデータ配列を指す新しいスライス記述子を返す必要があります。ただし、AddOption の 2 番目の引数として渡される値は、スライス記述子へのポインタまたはスライス記述子の本物のコピーのいずれかである可能性があります。

動作の明確化

この質問に対する答えは、スライスのデータ型とその実際の表現を区別することにあります。スライス記述子は、長さと容量の 2 つの整数と、基になるデータへのポインタで構成されます。

append() は、潜在的に異なる基になるデータ配列を持つ新しいスライス記述子を返しますが、データへのポインタは、容量を拡張しない限りはそのままです。これは、スライス記述子自体はコピーですが、ポインタ値 (基になるデータへのアドレス) が共有されることを意味します。

追加の例

説明のために、次のように考えます。コード スニペット:

<code class="go">package main

import "fmt"

func main() {
    s := make([]int, 0, 5)
    s = append(s, []int{1, 2, 3, 4}...)

    a := append(s, 5)
    fmt.Println(a)

    b := append(s, 6)
    fmt.Println(b)
    fmt.Println(a)
}</code>
ログイン後にコピー

このコードは次のように出力します:

[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 6]
ログイン後にコピー

最初、 s には十分な容量があるため、a と b は同じ基礎となるデータ ポインターを共有します。ただし、s の容量を 4 に減らすと、出力は次のように変わります。

[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 5]
ログイン後にコピー

これは、s に十分な容量がある場合にのみ、a と b が同じ基礎となるデータを共有することを示しています。

以上がGolang の「append()」が、チャネル経由で送信されたスライスを変更しているように見えるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!