• Go语言指针简介


    在本教程中,我们将了解指针在 Go 中的工作原理,还将了解 Go 指针与其他语言(例如 C 和 C++)中的指针有何不同。

    什么是指针?

    指针是一个变量,它存储另一个变量的内存地址

    Go 中的指针

    在上图中,变量b具有值156并存储在内存地址 处0x1040a124。该变量a保存 的地址b。现在a指向b

    声明指针

    T是指向T*类型值的指针变量的类型。

    让我们编写一个声明指针的程序。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        b := 255
        var a *int = &b
        fmt.Printf("Type of a is %T\n", a)
        fmt.Println("address of b is", a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Run in playground

    **&**运算符用于获取变量的地址。

    在上述程序的第 9 行中,我们将b地址分配给a类型为 *int的对象。现在 a 被认为指向 b。当我们打印a 中的值时,则会输出b的地址,该程序输出

    Type of a is *int  
    address of b is 0x1040a124  
    
    • 1
    • 2

    您可能会得到 b 的不同地址,因为 b 的位置可以位于内存中的任何位置。

    指针的零值

    指针的零值是nil

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        a := 25
        var b *int
        if b == nil {
            fmt.Println("b is", b)
            b = &a
            fmt.Println("b after initialization is", b)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Run in playground

    在上面的程序中b最初是nil,然后它被分配给a的地址。该程序输出

    b is   
    b after initialisation is 0x1040a124  
    
    • 1
    • 2

    使用new函数创建指针

    Go 还提供了一个方便的函数new来创建指针。该new函数接受一个类型作为参数,并返回一个指向作为参数传递的类型的新分配的零值的指针。

    下面的例子会让事情变得更清楚。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        size := new(int)
        fmt.Printf("Size value is %d, type is %T, address is %v\n", *size, size, size)
        *size = 85
        fmt.Println("New size value is", *size)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Run in playground

    在上面的程序中, 我们使用new函数创建类型的指针int。该函数将返回一个指向新分配的类型零值的指针intint类型的零值为0。因此 size 将是 type*int并将指向

    上面的程序会打印

    Size value is 0, type is *int, address is 0x414020  
    New size value is 85  
    
    • 1
    • 2

    取消引用指针

    取消引用指针意味着访问指针指向的变量的值。

    让我们看看这在程序中是如何工作的。

    package main  
    import (  
        "fmt"
    )
    
    func main() {  
        b := 255
        a := &b
        fmt.Println("address of b is", a)
        fmt.Println("value of b is", *a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Run in playground

    在上述程序的第 10 行中,我们引用a并打印它的值。正如预期的那样,它打印了 b 的值。程序的输出是

    address of b is 0x1040a124  
    value of b is 255  
    
    • 1
    • 2

    我们再编写一个程序,在其中使用指针更改值。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        b := 255
        a := &b
        fmt.Println("address of b is", a)
        fmt.Println("value of b is", *a)
        *a++
        fmt.Println("new value of b is", b)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Run in playground

    在上述程序中,我们将a指向的值递增1,这会更改 b 的值*,因为 a 指向 b*。因此 b 的值变为 256。程序的输出为

    address of b is 0x1040a124  
    value of b is 255  
    new value of b is 256  
    
    • 1
    • 2
    • 3

    将指针传递给函数

    package main
    
    import (  
        "fmt"
    )
    
    func change(val *int) {  
        *val = 55
    }
    func main() {  
        a := 58
        fmt.Println("value of a before function call is",a)
        b := &a
        change(b)
        fmt.Println("value of a after function call is", a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Run in playground

    在上面的程序中, 我们将保存 a 地址的b 指针变量 传递给函数change。在函数内部change,在第 8 行使用引用来更改。该程序输出,

    value of a before function call is 58  
    value of a after function call is 55  
    
    • 1
    • 2

    从函数返回指针

    函数返回局部变量的指针是完全合法的。Go编译器足够智能,它会在堆上分配这个变量。

    package main
    
    import (  
        "fmt"
    )
    
    func hello() *int {  
        i := 5
        return &i
    }
    func main() {  
        d := hello()
        fmt.Println("Value of d", *d)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Run in playground

    在上面程序的第 9 行中,我们从hello函数返回局部变量的地址。

    此代码的行为在 C 和 C++ 等编程语言中是未定义的,因为i一旦hello函数返回,变量就会超出范围。

    **但就 Go 而言,编译器会进行转义分析,并在i地址转义本地作用域时在堆上进行分配。**因此这个程序将运行并打印,

    Value of d 5  
    
    • 1

    不要将指向数组的指针作为参数传递给函数。使用切片代替。

    假设我们想要对函数内的数组进行一些修改,并且函数内对该数组所做的更改应该对调用者可见。实现此目的的一种方法是将数组的指针作为函数的参数传递。

    package main
    
    import (  
        "fmt"
    )
    
    func modify(arr *[3]int) {  
        (*arr)[0] = 90
    }
    
    func main() {  
        a := [3]int{89, 90, 91}
        modify(&a)
        fmt.Println(a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Run in playground

    在上述的程序中,我们将a数组的地址传递给modify函数。在modify函数的第 8 行中,我们取消引用 arr 并分配90给数组的第一个元素。该程序输出

    [90 90 91]
    
    • 1

    a[x] 是 (*a)[x] 的简写。所以上面程序中的 (*arr)[0] 可以替换为 arr[0]。让我们使用这种简写语法重写上面的程序。

    package main
    
    import (  
        "fmt"
    )
    
    func modify(arr *[3]int) {  
        arr[0] = 90
    }
    
    func main() {  
        a := [3]int{89, 90, 91}
        modify(&a)
        fmt.Println(a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Run in playground

    该程序还输出[90 90 91]

    尽管这种将数组指针作为参数传递给函数并对其进行修改的方法是有效的,但这并不是 Go 中实现此目的的惯用方法。我们为此准备了slices

    让我们使用slice重写相同的程序。

    package main
    
    import (  
        "fmt"
    )
    
    func modify(sls []int) {  
        sls[0] = 90
    }
    
    func main() {  
        a := [3]int{89, 90, 91}
        modify(a[:])
        fmt.Println(a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Run in playground

    在上面程序的第 13 行中,我们将一个切片传递给函数modify。切片的第一个元素更改为90。该程序还输出[90 90 91].

    因此,如果想将指针传递给周围的数组并使用切片来代替。这段代码更加简洁,并且符合 Go 语言习惯

    Go不支持指针运算

    Go 不支持 C 和 C++ 等其他语言中存在的指针算术。

    package main
    
    func main() {  
        b := [...]int{109, 110, 111}
        p := &b
        p++
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Run in playground

    上面的程序会抛出编译错误main.go:6: invalid opera: p++ (non-numeric type *[3]int)

    这就是 Go 中的指针。祝你有美好的一天。

  • 相关阅读:
    【HDOJ2586】【Tarjan离线求LCA】
    常用命令记录
    HCIP学习笔记-存储服务规划 -4
    线上培训 第19期 | Zemax ZPL 线上培训 招生啦
    C++转换函数
    java class 文件格式解析
    git初级
    【Java集合类】之 HashSet以及底层逻辑分析
    CSDN客诉周报第6期|本周解决25个用户问题,修复1个漏洞,收到4条建议
    Java Interview 200 Questions —— Day01 —— 备战2022年秋招 —— 经典 200 问
  • 原文地址:https://blog.csdn.net/qq497811258/article/details/133882143