スライスの追加と元のスライスに対するその影響について
Go でスライスを操作する場合、新しい要素を追加するために append 関数がよく使用されます。既存のスライスに。ただし、多くの開発者は、この追加操作で元のスライスも変更できることを知って驚くかもしれません。
調査中のコード
次のコード スニペットを考えてみましょう:
func someFunc(A []int) int { for i := 0; i < len(A); i++ { tempA := A // copy the slice by value fmt.Println("A: ", A) fmt.Println("tempA: ", A) fmt.Println() newArr = remove(tempA, i) if isAesthetic(newArr) { ways++ } } } func remove(slice []int, s int) []int { return append(slice[:s], slice[s+1:]...) }
ここで、someFunc 関数はスライス A を入力として受け取り、次に tempA という名前の A のコピーを作成してから呼び出します。 tempA から要素を削除する削除関数。動作中の関数を検査すると、次のコンソール出力に気づくかもしれません:
A: [3 4 5 3 7] tempA: [3 4 5 3 7] A: [4 5 3 7 7] tempA: [4 5 3 7 7] A: [4 3 7 7 7] tempA: [4 3 7 7 7] A: [4 3 7 7 7] tempA: [4 3 7 7 7]
驚くべき副作用
コードが実行されると、次の内容が出力されます。 A と tempA の両方で、tempA で append が呼び出された後に元のスライス A も変更されることがわかります。 A の値によるコピーは tempA に加えられた変更から独立していると予想されるため、この動作は一見直観に反しているように見えるかもしれません。
ただし、この現象はスライスの実装方法の直接的な結果です。囲碁で。スライスは本質的に、ヘッダーと基礎となる配列へのポインターで構成される軽量のデータ構造です。ヘッダーにはスライスの長さと容量に関する情報が含まれており、ポインターはスライス内の最初の要素を指します。
A の値を tempA に割り当てると、基本的には、次を指す新しいスライス ヘッダーを作成することになります。 A と同じ基礎となる配列です。したがって、両方のスライスが同じデータを参照しているため、tempA に加えられた変更は A にも反映されます。
理解スライス ヘッダーと配列
この動作をさらに理解するには、Go でスライス ヘッダーと配列がどのように相互作用するかを理解するのに役立ちます。配列には、同じ型の要素の連続したブロックが含まれます。一方、スライスは、配列のセクションの動的なビューを提供します。これは配列内の連続した要素セットを記述しますが、基礎となる配列データを所有しません。
構文 []T{e1, e2, ..., を使用して配列からスライスを作成する場合en} を使用すると、基本的には配列の最初の要素を指す新しいスライス ヘッダーを作成することになります。スライスの長さは n に設定され、容量はスライス後の配列の残りの長さに設定されます。
同様に、構文 []T(arr) を使用してスライス ヘッダーを作成すると、 arr と同じ基礎となる配列を指すスライスを作成しています。スライスの長さは arr の長さに設定され、容量は arr の容量に設定されます。
影響とベスト プラクティス
スライスと配列の関係を理解すると、潜在的な落とし穴を回避し、より効率的な Go コードを作成するのに役立ちます。スライスを操作するときは、次の点に注意してください。
Go スライスの内部を理解すると、コードが意図したとおりに動作することを確認しながら、その柔軟性と効率性を活用できるようになります。スライス ヘッダーと配列の微妙な違いを理解することで、Go でのスライスの技術をマスターし、この多用途なデータ構造の可能性を最大限に引き出すことができます。
以上がGo スライスのコピーに追加すると、元のスライスも変更されるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。