web-dev-qa-db-ja.com

golangスライスは値渡しされますか?

Golangでは、巡回セールスマン問題のスクランブルスライス関数を作成しようとしています。これをしている間、スライスを編集し始めたときに、スクランブル機能を渡すたびにスクランブル機能が異なることに気付きました。

デバッグ後、関数内のスライスを編集したことが原因であることがわかりました。しかし、Golangは「値渡し」言語であると想定されているため、これはどのように可能ですか?

https://play.golang.org/p/mMivoH0TuV

私が意味することを示すために遊び場リンクを提供しました。 27行目を削除すると、そのままにしておくのとは異なる出力が得られます。引数として渡された場合、関数はスライスの独自のコピーを作成することになっているため、違いはありません。
誰かが現象を説明できますか?

32
duck

はい、Goのすべては値渡しされます。スライスも。ただし、スライス値はheaderであり、バッキング配列の連続セクションを記述し、スライス値には要素が存在する配列へのポインタのみが含まれます実際に保存されます。スライス値には要素が含まれません(配列とは異なります)。

したがって、スライスを関数に渡すと、同じバッキング配列を指すポインターを含む、このヘッダーからコピーが作成されます。スライスの要素を変更すると、バッキング配列の要素が変更されるため、同じバッキング配列を共有するすべてのスライスが変更を「観察」します。

スライスヘッダーの内容を確認するには、 reflect.SliceHeader タイプ:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

関連する/可能な重複する質問を参照してください: Golang関数パラメーターはコピーオンライトとして渡されますか?

ブログ投稿を読む: Go Slices:usage and internals

81
icza