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 サイトの他の関連記事を参照してください。