Go の「追加」関数での意図しない上書き
Go では、スライスへの追加の微妙な違いを調べると、予期しない結果が生じることがあります。この動作を示す例を詳しく見てみましょう。
次のコードを考えてみましょう。
import "fmt" type Foo struct { val int } func main() { a := make([]*Foo, 1) a[0] = &Foo{0} b := [3]Foo{Foo{1}, Foo{2}, Foo{3}} for _, e := range b { a = append(a, &e) } for _, e := range a { fmt.Printf("%v ", *e) } }
興味深いことに、{0} {1} {2} {3} を出力する代わりに、出力が{0} {3} {3} {3} と読みます。このパズルを解くために、「append」関数を詳しく見てみましょう。
Go では、「append」は要素を追加するスライスへのポインターを期待します。この例では、スライス「a」に「Foo」インスタンスへのポインタを正しく格納しています。
しかし、配列「b」の要素をループするときに、微妙な問題が発生します。 「範囲」構文は、実際の要素自体ではなく、各要素のコピーを反復処理します。その結果、最終反復で「Foo{3}」を指す「Foo」インスタンス「e」のコピーへのポインタを一貫して追加しています。
この動作を修正するには、以下を参照する必要があります。 「append」操作における「b」の実際の要素:
for i := range b { a = append(a, &b[i]) }
この変更により、「b」内の要素にポインタを直接追加できるようになり、目的の出力が得られます。 {0} {1} {2} {3}.
この動作の根本的な理由を理解することが重要です。 Go では、ポインタは値であり、データを操作するときにメモリ アドレスとして解釈されます。そのため、「range」ループは、元のオブジェクトではなく、値のコピーを反復処理します。この知識は、最初のコードで 3 つの要素が同じ "Foo{3}" インスタンスを指していた理由を理解するのに役立ちます。
以上がGo の「append」関数が範囲ループからポインターを追加すると予期しない結果が生じるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。