zoukankan      html  css  js  c++  java
  • 第二章 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

  • 相关阅读:
    Nginx 部署多个 web 项目(虚拟主机)
    Nginx 配置文件
    Linux 安装 nginx
    Linux 安装 tomcat
    Linux 安装 Mysql 5.7.23
    Linux 安装 jdk8
    Linux 安装 lrzsz,使用 rz、sz 上传下载文件
    springMVC 拦截器
    spring 事务
    基于Aspectj 注解实现 spring AOP
  • 原文地址:https://www.cnblogs.com/ITPower/p/12047190.html
Copyright © 2011-2022 走看看