go言語ノート——スライスの底の本質は共有配列メモリです!!!sliceスライス自体をポインタで指さすのは絶対にやめましょう.もう一つの参照タイプはポインタです.

3217 ワード


転入先https://www.cnblogs.com/bonelee/p/6862377.html
スライス
スライス(slice)は、配列に対する連続フラグメントの参照である(この配列は、相関配列と呼ばれ、通常は匿名である)ため、スライスは参照タイプである(したがって、C/C++の配列タイプ、またはPythonのlistタイプに類似している).
スライスは、長さが可変な配列です.
複数のスライスは、同じ配列のフラグメントを表す場合、データを共有することができます.したがって、1つのスライスと関連配列の他のスライスは共有記憶され、逆に、異なる配列は常に異なる記憶を表す.配列は実際にはスライスされたコンストラクションブロックです.
利点は、スライスが参照であるため、追加のメモリを使用する必要がなく、配列を使用するよりも効率的であるため、Goコードではスライスが配列よりも一般的である.
スライスを宣言するフォーマットは、var identifier []typeです(長さは説明する必要はありません).
1つのスライスは、初期化されない前にデフォルトでnilであり、長さは0です.
スライスは、同様の配列で初期化することもできる:var x = []int{2, 3, 5, 7, 11}.これにより、長さ5の配列が作成され、関連するスライスが作成されます.
メモリ内のスライスの組織方式は、実際には、関連配列を指すポインタ、スライス長、およびスライス容量の3つのドメインを持つ構造体です.次の図は、長さ2、容量4のスライスを示しています.メモリの共有方法に注意してください!!!
  • y[0] = 3y[1] = 5.
  • スライスy[0:4]は元素3,5,7,11からなる.

  •  
    注意ポインタでsliceを指ささないでください.スライス自体はすでに参照タイプなので、それ自体がポインタです!!
    7.2.2スライスを関数に渡す
    関数が配列を操作する必要がある場合は、常にパラメータをスライスとして宣言する必要があります.関数を呼び出すと、配列をスライスしてスライス参照として作成し、関数に渡します.配列要素の和を計算する方法があります.
    func sum(a []int) int {
    	s := 0
    	for i := 0; i < len(a); i++ {
    		s += a[i]
    	}
    	return s
    }
    
    func main() {
    	var arr = [5]int{0, 1, 2, 3, 4}
    	sum(arr[:])
    }

    7.2.3 make()でスライスを作成する
    相関配列がまだ定義されていない場合、make()関数を使用して、var slice1 []type = make([]type, len)の相関配列を作成しながらスライスを作成できます.
    次の図は、makeメソッドを使用して生成されたスライスのメモリ構造を示しています.
    7.2.4 new()とmake()の違い
    どちらも違いがなく、スタックにメモリを割り当てているように見えますが、動作が異なり、異なるタイプに適しています.
  • new(T)は、新しいタイプTごとにメモリを割り当て、0に初期化し、*Tのタイプのメモリアドレスを返す.この方法は、配列や構造体などの値タイプに適用されるタイプT、値0のアドレスを指すポインタを返す(10章参照).&T{}に相当します.
  • make(T)は、スライス、map、channelの3つの組み込み参照タイプにのみ適用されるTの初期値を返します(8章、13章参照).

  • 言い換えれば、new関数はメモリを割り当て、make関数は初期化される.次の図は、違いを示しています.
    図7.3の第1の図では、
    var p *[]int = new([]int) // *p == nil; with len and cap 0
    p := new([]int)

    2枚目の図では、p := make([]int, 0)で、スライスは初期化されたが、空の配列を指している.
    以上の2つの方式は実用性が高くない.次の方法:
    var v []int = make([]int, 10, 50)

    または
    v := make([]int, 10, 50)

    これにより、50個のint値を有する配列が割り当てられ、配列の最初の10要素を指す長さ10、容量50のスライスvが作成される.
    変換元:https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/07.2.md
     
    7.6.3文字列とスライスのメモリ構造
    文字列string s = "hello"およびサブ文字列t = s[2:3]のメモリ内の構造は、次の図で表すことができる.
     
    7.6.8スライスとゴミ回収
    スライスの最下位は、スライスで定義された容量よりも実際の容量が大きい配列を指します.スライス指向がない場合にのみ、最下位の配列内レイヤが解放され、プログラムが余分なメモリを消費する場合があります.
    例示的な関数FindDigitsは、1つのファイルをメモリにロードし、すべての数値を検索してスライスを返します.
    var digitRegexp = regexp.MustCompile("[0-9]+")
    
    func FindDigits(filename string) []byte {
        b, _ := ioutil.ReadFile(filename)
        return digitRegexp.Find(b)
    }

    このコードはスムーズに実行できますが、[]byteが返す最下位はファイル全体のデータです.返されるスライスが解放されない限り、ゴミ回収器はファイル全体に消費されるメモリを解放できません.言い換えれば、わずかな有用なデータがファイル全体のメモリを消費しています.
    この問題を回避するには、必要な部分を新しいスライスにコピーします.
    func FindDigits(filename string) []byte {
       b, _ := ioutil.ReadFile(filename)
       b = digitRegexp.Find(b)
       c := make([]byte, len(b))
       copy(c, b)
       return c
    }