Golangポインター

5.6ポインター

5.6.1ポインタとは

  • ポインタは変数であり、別の変数のメモリアドレスを格納するために使用されます
func main() {
    // 指针地址,指针类型,指针取值
    // &取地址,*根据地址取值
    a := 10
    b := &a
    fmt.Println(*b)

    //指针类型 *int *unit *float *string *array *struct 等
    
    // 指针的定义
    // var 变量名 指针类型
    var p1 *int  //定义空指针
    p1 = &1
}

5.6.2配列ポインターとポインター配列

  • 配列ポインタ:配列のメモリアドレスを格納するために使用されるポインタ
func main() {
    arr := [4] int{1, 2, 3, 4}
    fmt.Println(arr)
  
    // 定义一个数组指针
    var p1 *[4] int
    fmt.Println(p1)  // nil 空指针
    fmt.Ptintf("%T\n", p1)  // *[4] int
    
    p1 = &arr1
    fmt.Println(p1)
    fmt.Printf("%p\n", p1)
    fmt.Printf("%p\n", &p1)
    
    // 通过数组指针操作数组
    (*p1)[0] = 100
    // 可以简写为
    // p1[0] = 200
    fmt.Println(arr)  // [100 2 3 4]
}
  • ポインタ配列:要素をポインタとして持つ配列です
func main() {
    a := 1
    b := 2
    c := 3
    d := 4
    arr1 := [4] int{a, b, c, d}
    arr2 := [4] *int{&a, &b, &c, &4}
    fmt.Println(arr1)
    fmt.Println(arr2)
    
    // 操作数组与指针数组的区别
    //arr1[0] = 100
    //fmt.Println(a)
    //fmt.Println(arr1, &arr1[0])  // 值类型,将a,b,c,d的值拷贝过来放到数组中,修改的是数组空间内的数据,与a,b,c,d没有关系
    //fmt.Println(arr2)
    
    *arr2[0] = 1000
    fmt.Println(a)
    fmt.Println(arr1, &arr1[0])
    fmt.Println(arr2, *arr2[0])       // 通过指针数组中的内存地址,修改了a,b,c,d的值,类似于Python列表
    
    b = 2000
    fmt.Println(b)
    fmt.Println(arr1, &arr1[1])
    fmt.Println(arr2, *arr2[1])
}

要約:

配列は値のタイプであり、値を数値メモリにコピーします。2つは互いに独立しており、互いに影響しません。配列が変更された後、配列メモリの値は変更されますが、コピーされたソースデータ、ソースデータは変更されません。アレイに影響します

Goのスライスは、元の配列への参照です。2つは相互に関連しています。スライス要素の値が変更されると、それに関連付けられている基になる配列も影響を受けます。同様に、基になる配列での変更もスライスの値に影響します。

Pythonのリストは参照型です。ポインター配列に基づいて、変数要素(変数型と不変型)を変更した後、参照されるソースデータも影響を受けます---

  • GoのスライスとPythonのリストの違い
    • Goのスライスには同じタイプのメンバーがあり、Pythonリストとタプルはタイプを制限しません。
    • どちらの言語にも[a:b]このようなスライス操作があり、意味は似ていますが、goのaとbの2つのパラメーターを負の数にすることはできず、pythonを負の数にすることができます。
    • どちらの言語にもこのスライス操作[a:b:c]がありますが、意味はまったく異なります。Goのcは容量を意味し、pythonのcはステップサイズを意味します
    • Pythonスライスは新しいオブジェクトを生成し、新しいオブジェクトのメンバーに対する操作は古いオブジェクトに影響しません。goスライスは古いオブジェクトの一部への参照を生成し、そのメンバーに対する操作は古いオブジェクトに影響します。
    • 基礎となる実装の違い
      • ゴーのスライス、最下層はトリプル、ポインター、長さ、容量です。ポインタは連続するメモリの一部を指し、長さは既存のメンバーの数、容量はメンバーの最大数です。スライスするときは、通常、新しいメモリには適用されませんが、元のポインタを移動し、新しい長さと容量でスライスタイプの値を形成して戻ります。つまり、goのスライス操作は通常、スライスを生成したスライスとメモリを共有します。スライスだけでなく、文字列と配列のスライスもあり、通常はメモリを共有します。もちろん、異常もあります。つまり、スライスが大きすぎるときに提供される容量です。このとき、新しいメモリが適用されてコピーされるか、追加によってスライスの容量を超えます。このとき、新しいスライスは古いスライスとメモリを共有しません。(スライス/作成時に指定した容量が長さより短い場合、パニックになります)
      • Pythonのリストは、実際にはポインタの配列です。もちろん、下層には空室がありますが、基本的には配列です。それらをスライスすると、新しい配列が作成されます。これは、新しい配列を作成することです。Pythonのリストには容量の概念がありません。これは実際には、スクリプト言語とコンパイルされた言語の違いを反映しています。どちらの言語にも同様のスライス操作がありますが、Pythonの主な目的は利便性です。goの主な目的は高速です(そして、ポインター操作を破棄することの欠点を補います)。

5.6.3関数ポインターとポインター関数

// 函数指针
// Go中函数默认就是一个指针类型,不需要*
func main() {
    var a func()
    a = func1
    a()
}
func func1() {
    fmt.Println("这是func1()")
}

// 指针函数
// 返回值为指针的函数
func main() {
    // arr1是数组,值传递,将func1中返回的arr的值拷贝到arr1中,当func1调用结束,arr被销毁
    arr1 := func1()
    fmt.Printf("arr1的类型:%T,内存地址:%p,值:%v\n",arr1, &arr1, arr1)
    
    // arr2是指针类型,值传递,将func2中返回的arr的内存地址保存到arr2中,arr不会随着func2的结束而销毁(和闭包一样改变了变量的生命周期?)
    arr2 := func2()
    fmt.Printf("arr2的类型:%T,内存地址:%p,值:%v\n",arr2, &arr2, arr2)
}

// 这是普通函数
func func1() {
    arr := [4] int{1, 2, 3, 4}
    return arr
}

// 这是指针函数
func func2() *[4] int {
    arr := [4] int{1, 2, 3, 4}
    return &arr
}        

5.6.4パラメータとしてのポインタ

func main() {
    /*
    指针作为参数
    参数:值传递和引用传递
    
    总结:值传递,拷贝一份,不会影响原数据,但消耗内存
         引用传递通过指针操作数据,会改变原数据,但节省内存(拷贝的数据可能很大)
    */
    n := 10
    // 值传递,func1改变不会影响n,a随着func1结束而被销毁
    func1(n)
    fmt.Println(n)

    // 引用传递,将n的内存地址拷贝到a中,通过*a更改了n,a也会随着func2的结束而被销毁,但n已经改变
    func2(&n)
    fmt.Println(n)
}

func func1(a int) {
    a = 100
    fmt.Println(a)
}

func func1(a *int) {
    *a = 100
    fmt.Println(*a)

おすすめ

転載: www.cnblogs.com/zj420255586/p/12755163.html