ホームページ > バックエンド開発 > Golang > for-range ループからポインターを追加すると、Go の「append」関数が予期しない結果を生成するのはなぜですか?

for-range ループからポインターを追加すると、Go の「append」関数が予期しない結果を生成するのはなぜですか?

Linda Hamilton
リリース: 2024-12-22 22:35:15
オリジナル
756 人が閲覧しました

Why does Go's `append` function produce unexpected results when appending pointers from a for-range loop?

Go スライスでの予期しない追加動作: 説明

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}。この矛盾の背後にある理由は、for ループの範囲変数の性質にあります。

範囲変数を理解する

Go の for-range ループは、コピーを作成することによって動作します。反復される配列またはスライス内の各要素の。この場合、e は配列 b の要素のコピーです。したがって、e 自体は b の要素ではありません。むしろ、これは要素の値を保持する一時変数です。

スライス a に追加するとき、コードは b の実際の要素のアドレスではなく、e のアドレスを追加します。 e はすべての反復で同じコピーであるため、同じポインタが 3 回 a に追加されます。したがって、e に割り当てられた最後の値 (Foo{3}) は、繰り返し出力される値です。

動作の修正

この動作を修正するには、コードe のアドレスではなく、b の実際の要素のアドレスを追加する必要があります。修正されたループは次のようになります。

for i := range b {
    a = append(a, &b[i])
}
ログイン後にコピー

&e の代わりに &b[i] を追加することで、コードは b の各要素が a に追加されるようにします。その結果、正しい出力 {0} {1} {2} {3} が出力されます。

初期動作の理由

この予期しない動作は、 Go における真の参照の数。 Go にはポインター型と非ポインター型がありますが、参照はありません。範囲変数は単なるローカル変数であり、ポインターまたは非ポインターの値を保持します。参照を保持することはできません。

したがって、範囲変数を操作するときは、要素自体ではなく、値のみを操作することになります。実際の要素を変更するには、値を範囲変数に代入して、値を効果的にコピーする必要があります。この値は次の反復で上書きされます。

以上がfor-range ループからポインターを追加すると、Go の「append」関数が予期しない結果を生成するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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