go 指针

4 指针

指针变量是要接收一个地址的,也就是 var ptr *int = &num 这串代码

4.1 指针基本介绍:

4.1.1 基本数据类型,变量存的就是值,也叫值类型。

  • 查看变量在内存中的地址范例:

    package main
    
    import (
        "fmt"
    )
    
    // 演示 golang 中指针类型
    func main()  {
        
        // 基本数据类型在内存中的布局
        var i int = 10
        
        // 我们怎么将 i 变量的地址取出
        fmt.Println("i 的地址 = ",&i)
    }

    CMD 输出结果

  • 如下图我们分析一下基本数据类型在内存中是怎么存在的,以及内存的布局。

我们这里定义一个变量 i 为 int 类型且值为 10 , 这个 i 的变量直接和一个存空间关联起来,如黄色区域就是我们内存的一个空间里面存放了数据 10 ,i 这个变量就直接可以引用到和 10 的这个空间关联起来,但是这个黄色区域 10 本身在内存中是有地址的,那我们怎么可以拿到这个地址呢?

意思是 i 的变量代表着 10 这个值,可是 10 这个值在内存中分配,那就意味着 10 这个值的空间本身在内存中有个地址。也就是说 10 这个空间本身的地址就是 0xc0000100b0 。而 i = 10 所以 i 的地址就是 0xc0000100b0 ,等于说我们的变量 i 取出来就是 0xc0000100b0 这个值,以上就是所说的基本数据类型在内存中布局的这么一个情况。

4.1.2 获取变量在内存中的地址

获取变量的地址,用& 符号,比如:var num int, 获取 num 在内存中的地址就需要使用:&num

4.1.3 指针类型是什么

指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值

比如:

var ptr *int = &num 这里的 ptr 就是一个指针的名字,并且指向一个 int 前面使用了 * 号来区分,有这个* ptr 就是一个指针,如果没有这个 * 那么这个 ptr 只是一个 int 数据类型。

举例说明指针在内存中的存在形式:

package main

import (
    "fmt"
)

// 演示 golang 中指针类型
func main()  {
    
    var i int = 10  // 给 i 定义为 int 类型值为 10
    
    // var ptr *int = &i 表示的含义有以下几点
    // 1、 ptr 是一个指针变量
    // 2、 ptr 的类型是一个 *int ,也就是说它是指向一个 int 类型的一个指针
    // 3、 ptr 本身的值为 &i
    var ptr *int = &i
    fmt.Printf("ptr=%v", ptr)   // 这是将 ptr 的原值输出
}

CMD 输出结果:

由此可以发现 ptr 和刚才 i 变量在内存中的地址是相同的。

指针在内存中的地址查询:

指针本身也有一个地址。我们可以通过下面的代码查看:

package main

import (
    "fmt"
)

// 演示 golang 中指针类型
func main()  {
    
    // 基本数据类型在内存中的布局
    var i int = 10

    // var ptr *int = &i 表示的含义有以下几点
    // 1、 ptr 是一个指针变量
    // 2、 ptr 的类型是一个 *int ,也就是说它是指向一个 int 类型的一个指针
    // 3、 ptr 本身的值为 &i
    var ptr *int = &i
    fmt.Printf("ptr 的地址 =%v", &ptr)
}

CMD 输出结果:

ptr 在内存中的地址为 0xc000006028

4.1.3.1 指针在内存中的布局图文解析:

此时此刻我们这里有个变量叫做 ptr 是一个指针变量,ptr 本身会存放一个地址。会指向蓝色区域的这个空间,而这个蓝色空间中存放的值就是 10 在内存中的值 0xc0000100b0 ,蓝色这个空间真正的指向了黄色这块空间,也就是真正 10 这个值。当然指针也需要在内存中有个地址来存放,也就是说蓝色区域是 ptr 指针存放在内存中的空间,这块空间在内存中的地址为 0xc000006028 。然后这蓝色区域的内存空间用来存放的是 10 这个值在内存中的地址0xc0000100b0。因为 ptr 是一个指针变量只能够存放地址,这个地址就是 10 这个值的地址也就是指向了真正的空间 0xc0000100b0

也就是说 ptr 指针变量的值为 0xc0000100b0 ,ptr 指针变量内存地址为 0xc000006028,以上就是指针在内存中的布局。

4.1.4 获取指针类型所指向的值

获取指针类型所指向的值,使用 * 号来指向,比如:var ptr *int,使用 *ptr 就能够取出 ptr指向的这个值

范例如下:

package main

import (
    "fmt"
)

// 演示 golang 中指针类型
func main()  {
    
    // 给 i 赋值为 10
    var i int = 10
    
    // var ptr *int = &i 表示的含义有以下几点
    // 1、 ptr 是一个指针变量
    // 2、 ptr 的类型是一个 *int ,也就是说它是指向一个 int 类型的一个指针
    // 3、 ptr 本身的值为 &i
    var ptr *int = &i

    // 使用 *ptr 也就是说告诉编译器,将 ptr 指针指向的那个值取出,就是 &i 的值
    // 实际上第一步还是将 ptr 存放的地址取出来了,然后这里的 * 就把这个地址所对应的值返回输出
    fmt.Printf("ptr 指向的值 = %v",*ptr) 
}

CMD 输出结果:

通过以上代码的方式就可以去除 ptr 指针说指向的值也就是 &i 的值 10

4.2 指针案例演示练习题

4.2.1 第一题

写一个程序,获取一个 int 变量 num 的地址,并显示到终端

答案代码如下

package main

// 调用 fmt 包
import (
    "fmt"
)

func main()  {

    // 给 num 定义为 int 数据类型
    var num int
    
    // 通过 println 函数的 &num 将 num 在内存中的地址取出
    fmt.Println(" num 地址是 ",&num)
}

CMD 输出结果

通过上面这种方式就已经将 num 在内存中的地址取出

4.2.2 第二题

将 num 的地址赋给指针 ptr ,并通过 ptr 取修改 num 的值。

代码答案如下:

package main

import (
    "fmt"
)

func main()  {
    // 给 num 定义为 int 类型并且赋值为 9
    var num int = 9

    // 将 num 在内存空间中的地址进行输出
    fmt.Println(" num 地址是 ",&num)
    
    // 将 ptr 指针指向 num 的内存空间,也就是说现在 ptr 就是 num
    var ptr *int = &num
    
    // 然后修改 ptr 为 10 ,间接就将 num 改为了 10
    *ptr = 10

    // 通过 fmt 包中的 Println 函数输出 num 的值
    fmt.Println("num = ",num)
}

CMD 输出结果

由此可见 num 的值已经修改为了 10

如下图解释:

最开始有个 num 的变量,这个 num 指向了一个空间,这个空间存的值就是 9 ,紧接着 ptr 指针指向 9 这个空间的地址,当我们的的代码执行完 var ptr *int = &num 的时候,在内存中也就是说有 ptr 这么一个指针变量,并且这个 ptr 指向了一个空间,这个空间里面存放了一个地址 0xc0000100b0,这个地址就是 num 存放数据的地址,这个数据也就是 9 ,因此 ptr 就指向了 9 。

接着到了 *ptr 这一步,ptr 现在实际上存放的 9 这个值的地址0xc0000100b0,然后再通过 * 一取这时候相当于说就指向了 0xc0000100b0 ,* ptr 就代表取到 9 这个 值然后我们再将 *ptr = 10 ,所以这时候 num 的值就变为了 10

这个时候输出 num 也就边为 10 了。

4.2.3 第三题

写出 a b *ptr 各个数的值


func main()  {
    var a int = 300     // 给 a 赋值 300
    var b int = 400     // 给 b 赋值 400
    var ptr *int = &a   // 将 ptr 指向 a
    *ptr = 100          // 再将 ptr 修改为 100 也就是说现在 a = 100
    ptr = &b            // 再次将 ptr 指向了 b
    *ptr = 200          // 然后再次修改 ptr 为 200 但是这时候 ptr 指向了 b 也就是说现在修改的就是 b 所以 b = 200 而 a 不受其影响
    
    fmt.Printf("a = %d,b = %d,*ptr = %d",a,b,*ptr)
}

////  最后结果
a = 100
b = 200
*ptr = 200 // 因为最后一次赋值为 200 所以取出最后一次的 *ptr 也就是 200

4.3 指针常见错误解析

4.3.1 指针变量接受地址错误写法

func main()  {
    var a int = 300

    // 错误指针变量是要接收一个地址而非变量
    var ptr *int = a  // 而这里要写成 var ptr *int = &a 即可正确
}

4.3.2 指针变量定义数据类型不匹配错误

再给一个指针的时候我们也需要考虑数据类型,也就是说指针定义的数据类型一定要和指定的变量数据类型一致即可。

func main()  {
    var a int = 300
    
    // 错误,因为这里 ptr 指针的类型定义的是 float32 而变量 a 的类型为 int,数据类型错误
    var ptr *float32 = &a   // 将其修改为 var ptr *int = &a 即可正确
}

4.4 指针的注意事项

指针的使用注意事项有以下两点:

1、值类型,所有的值类型都对应有指针类型,形式为 * 数据类型 这就是值类型所对应的指针类型。比如说 int 对应的指针就是 *intfloat32 对应的指针类型就是 *float ,依此类推。

2、值类型包括: 基本数据类型 int系列 ,float 系列,boolstring ,数组和结构体 struct

点赞