• 第二章 go指针


    一. 指针

    先来看一段代码

        var a int =2
        var pa *int
        pa = &a
        *pa = 3
        fmt.Println(a)    

    这里定义了一个int类型的变量a, 有定义了一个指针类型的变量pa, 让pa指向了a的地址. 然后修改了pa的值, 我们看看打印出来的a是什么:

    结果:  3

    下面开始描述指针

    1. 指针也是一种变量, 指针变量的定义方式, 和普通变量的定义方式一样

    2. go语言中的指针不能进行运算. 在c中,指针是可以运算的. 比如 &pa+2, 这在go中是不可以的. 还是刚才的例子,我们对指针进行运算, 编译就过不去

     

    3. go语言只有值传递, 没有引用传递. 如何实现go语言的引用传递呢, 配合指针.

    如果你想要传递一个int类型的数值,如果是直接传递过去. 那么就是值拷贝. 如果想要引用传递, 那么就要把指针传递过去.

    思考: go语言只有值传递. 比如,定义一个方法, 然后调用它:

    func test(a int) {
    	a ++
    }
    func main() {
    a := 5
    test (a)
    fmt.Println("a: ", a)
    }

    结果: a: 5

      这里main方法调用test,将a拷贝了一份传给test. 这是值传递. 那么怎么才能让值跟着发生变化呢? 我们可以配合地址使用.

    func test(a *int) {
    	*a ++
    }
    
    func main() {
    	a := 5
    	test (&a)
    	fmt.Println("a: ", a)
    }

    结果: a: 6

      这是一个配合地址使用的值传递. 他的传递是将a的地址复制了一份,传给了test. 如下图: 

     他们最终指向的都是5. 所以, 一旦test方法修改ile值, main方法中的也会随之修改

     那么, 自定义类型是值传递还是引用传递呢?

     答案: 不一定. 如果你将一个对象Cache传递到一个函数里. 他是对这个Cache的值copy么? 不一定. 要看这个函数里面的内部结构: 看下面的例子.

    例子1:

    type Cache struct {
        aa int
        bb int
        cc string
    }
    
    func change(c Cache) {
        c.cc = "world"
        fmt.Println("in change, c", c)
    }
    
    func main() {
        // 初始化一个Cache对象
        var c = Cache{aa:1, bb:100, cc:"hello"}
        // 修改对象的值. 这里传递过去的是一个值拷贝
        change(c)
        fmt.Println(c)
    }

    这个例子, go语言只有值拷贝, 这是对对象的值拷贝的过程. 修改了函数体里面对象的属性, 对函数外没有影响, 

    结果: 

    in change, c {1 100 world}
    {1 100 hello}

    例子2:

    type Cache struct {
        aa int
        bb int
        cc string
    }
    
    func change(c *Cache) {
        c.cc = "world"
        fmt.Println("in change, c", c)
    }
    
    func main() {
        // 初始化一个Cache对象
        var c = Cache{aa:1, bb:100, cc:"hello"}
        // 修改对象的值. 这里传递过去的是一个值拷贝
        change(&c)
        fmt.Println(c)
    }

    这个例子和例1不一样的地方是, 函数的参数是一个指针. go语言只有值拷贝, 这里是将c的地址拷贝了一份传给了change. 这就达到了引用传递的效果, 修改函数体里面的值,外面也受影响.

    返回结果: 

    in change, c &{1 100 world}
    {1 100 world}

    例子3:

    type Cache struct {
        aa int
        bb int
        cc *string  // 结构体里面有一个指针对象
    }
    
    func change(c Cache) {
        *c.cc = "world"
        fmt.Println("in change, c", c.aa, c.bb, *c.cc)
    }
    
    func main() {
        // 初始化一个Cache对象
        cc := "hello"
        var c = Cache{aa:1, bb:100, cc:&cc}
        // 修改对象的值. 这里传递过去的是一个值拷贝
        change(c)
        fmt.Println(c.aa, c.bb, *c.cc)
    }

    这个例子和前两个例子不同的地方是: 结构体Cache中cc是一个地址. 他不是一个变量了. 那么这个时候. change函数修改了cc的值,会怎么样呢?

    结果: 

    in change, c 1 100 world
    1 100 world

    是的, cc是一个地址, 当对象c拷贝一份到cc里面的时候, Cache中的地址变量cc 直接复制一份到change. 本身cc就是地址, 所以函数内改变了cc所在地址的值, 那么函数外也会改变

    这就说明了, 结构体传递到函数里面, 到底是值传递还是引用传递呢? 和结构体内部的结构有关系.

     3. 用go语言实现交换两个变量的值.

     分析1: 

    func change(a int ,b int) {
    a, b = b, a
    fmt.Println("in change, a:", a, ", b:", b)
    }

    func main() {
    // 初始化一个Cache对象
    a, b := 3, 4
    change(a, b)
    fmt.Println(a, b)
    }

    上述方法返回值: 

    in change, a: 4 , b: 3
    3 4

    错误的原因在于, int类型是值传递, 修改内容的值, 对外部没影响. 所以返回的还是3, 4

    分析2: 地址传递

    func change(a *int ,b *int) {
    fmt.Println(fmt.Println("[change---1], a:", a, ", b:", b))
    a, b = b, a
    fmt.Println("[change--2], a:", a, ", b:", b)
    }

    func main() {
    // 初始化一个Cache对象
    a, b := 3, 4
    fmt.Println("[main --- 1]", &a, &b)
    change(&a, &b)
    fmt.Println("[main --- 2]", a, b)
    }

    先来看输出结果:

    [main --- 1] 0xc000096008 0xc000096010
    [change---1], a: 0xc000096008 , b: 0xc000096010
    48 <nil>
    [change--2], a: 0xc000096010 , b: 0xc000096008
    [main --- 2] 3 4

    出乎意料, 原本以为, main函数最后的输出会是4, 3. 我们发现, 结果并不是. 也就是说, 虽然传的是指针过去, 但是是对指针的一个copy,  这一点更说明了, 所有的变量都是值拷贝. 包括指针变量. 也是一个值拷贝

    分析3: 改变地址的值

    func change(a *int ,b *int) {
        fmt.Println(fmt.Println("[change---1], a:", a, ", b:", b))
        *a, *b = *b, *a // 把地址的值改变了
        fmt.Println("[change--2], a:", a, ", b:", b)
    }
    
    func main() {
        // 初始化一个Cache对象
        a, b := 3, 4
        fmt.Println("[main --- 1]", &a, &b)
        change(&a, &b)
        fmt.Println("[main --- 2]", a, b)
    }

    和三个不同之处, 是在change函数内, 修改了指针类型的变量的值. 输出结果是:

    [main --- 1] 0xc00001c0f8 0xc00001c100
    [change---1], a: 0xc00001c0f8 , b: 0xc00001c100
    48 <nil>
    [change--2], a: 0xc00001c0f8 , b: 0xc00001c100
    [main --- 2] 4 3

    我们看到, 最后的输出结果却是是4, 3

    分析4: 还有一种更简单的交换两个值的方式

    func change(a *int ,b *int) (int, int){
        return *b, *a
    }
    
    func main() {
        // 初始化一个Cache对象
        a, b := 3, 4
        fmt.Println("[main --- 1]", &a, &b)
        a,b = change(&a, &b)
        fmt.Println("[main --- 2]", a, b)
    }

    输出:

    [main --- 1] 0xc00001c0f8 0xc00001c100
    [main --- 2] 4 3

    分析5: 最简单的方式

    func change(a int ,b int) (int, int){
        return b, a
    }
    
    func main() {
        // 初始化一个Cache对象
        a, b := 3, 4
        fmt.Println("[main --- 1]", &a, &b)
        a,b = change(a, b)
        fmt.Println("[main --- 2]", a, b)
    }

    结果:

    [main --- 1] 0xc000096008 0xc000096010
    [main --- 2] 4 3

    从这里我们得出以下结论: 

    1. 指针类型的变量, 和普通变量一样, 是值传递. 

    2. 指针类型的变量, 要想修改变量的值, 需要使用指针的指针来改变. 其实,在指针里面, 是指针的指针就是值了. 那么, 我们的原则是, 不管他是什么, 只有修改的是指针, 那么就达到了引用传递的效果.

    指针对于我们来说, 方便好多, 但是也会产生很对疑问. 比如分析4和分析5, 他们为什么得到的结果是一只呢? 需要看一下他的内部到底是怎么交换的.

    aaa

  • 相关阅读:
    linux下查找文件中空行的行号
    JavaScript:jklyDB
    Chart:Amcharts
    Chart-template
    Miscellaneos:ISV
    杂项-模板
    Error-MVCr:找到了多个与 URL 匹配的控制器类型。如果多个控制器上的特性路由与请求的 URL 匹配,则可能会发生这种情况。
    Error-MVC:HTTP Error 500.19
    ASP.NET 网站管理工具
    HTML:Browser 对象
  • 原文地址:https://www.cnblogs.com/ITPower/p/12047190.html
Copyright © 2020-2023  润新知