Go の「append」関数による予期しない上書きの問題
Go では、追加の要素でスライスを拡張するために append 関数が頻繁に使用されます。ただし、構造体にポインターを追加するときに予期しない動作が発生することがあります。
次のコード スニペットを考えてみましょう:
import "fmt" type Foo struct { val int } func main() { var a = make([]*Foo, 1) a[0] = &Foo{0} var 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} を出力します。この予期しない動作は、Go の動作の基本的な側面に由来しています。
for Range ループにコピー
Go は、for range ループを使用してスライスと配列を反復処理し、コピーを作成します。反復中の各要素の。このコードでは、for range ループ変数 e は配列要素 b[i] の一時コピーを表します。残念ながら、この一時変数のアドレスをスライス a に追加します。
修正
この問題を解決するには、一時的なコピーではなく、元の配列要素を使用します。インデックス i を使用するように for ループを変更することで、正しいアドレスがスライスに追加されるようになります。
for i := range b { a = append(a, &b[i]) }
この変更により、期待される出力は {0} {1} {2} {3 になります。 }.
動作の理由
Go に真の参照がないことが原因この行動に。参照が使用される C または Java では、ループ変数は配列要素を直接参照するため、ループ変数を介した変更が可能になります。ただし、Go では、ループ変数は参照ではなく要素のコピーを保持する別個の変数です。したがって、ループ変数に加えられた変更は、元の配列要素には反映されません。
以上がGo の「append」関数でポインターを追加すると予期しない上書きが発生するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。