为什么需要指针

首先有一段代码:

package main

import "fmt"

func changeValue(p int){
    p = 10  // 尝试修改传递过来的参数的值
}

func main (){
    var a int = 1 
    changeValue(a) // 将a作为实参传入函数
    fmt.Println("a = ", a)  // 请问打印出的是多少
}

分析:

  1. 第10行新建变量a 这个变量,变量值为1

    1. 此时在内存中开辟了一个内存地址1,用来存放a这个变量的值:1
  2. 第11行当调用函数changeValue

    1. 首先创建形参p,分配一个内存地址2,用来存放形参p的初始化默认值:0
    2. 接着将实参a的值传递给形参p的内存地址2,替换掉默认值0,此时内存地址2存放的值为1
  3. 接着指定到第6行函数体内部,修改形参p的值为10,也就是将内存地址2中的值从1修改成10
  4. 第12行打印a的值,即内存地址1的值,因为内存地址1没有做任何改动,所以仍为1

这就是相当于值的传递,不是传递a的内存地址,而是仅仅传递了a的内存的值。

运行的结果:
image.png

指针的作用

现在想要修改函数体中的形参p达到修改变量a的值,这个时候就要使用到指针

代码修改成以下:

package main

import "fmt"

func changeValue2(p *int){ // 现在的p表示指针类型的一个变量
    *p = 10  // *p表示p指向的内存地址存储的值
}

func main (){
    var a int = 1
    changeValue2(&a) // 表示传递的为a的内存地址
    fmt.Println("a = ", a) 
}

分析:

  1. 第10行新建变量a 这个变量,变量值为1(与之前一样)

    1. 此时在内存中开辟了一个内存地址1,用来存放a这个变量的值:1
  2. 第11行当调用函数changeValue

    1. 首先形参p *int ,为指针类型的一个变量,分配一个内存地址2,内存地址初始化值也为0,但不是0这个值而是为0地址
    2. &a 传递给p,也就是说将现在 p *int的内存地址2存储的0地址修改成a变量的内存地址,所以现在的变量p内存存储的值为变量a的内存地址,也称为指针p指向a,p存的值为a的内存地址,所以通过P就能找到a。
  3. 第6行p代表p的内存存储的值,p表示p存储的内存地址所代表的变量的内存,p=10表示通过p存储的值(这个值为一个内存地址),找到存储的内存地址(也就是a的地址1)指向的内存(也就是a的值)修改成10
  4. 第12行打印a的值为10

运行结果:
image.png

指针经典案例:交换两个值

package main

import "fmt"

func swap(pa *int, pb *int) {
    var tmp int
    tmp = *pa // tmp = main函数中的a
    *pa = *pb // main中的a = main中的b
    *pb = tmp // main中的b = tmp
}

func main() {
    var a int = 10
    var b int = 1

    swap(&a, &b)
    fmt.Println("a = ", a," b = ", b)
}

运行结果:
image.png
成功交换。

二级指针

一般指针在go中就比较少见,二级指针更是少见。
二级指针案例:

func main() {
    var a int = 10
    var b int = 1

    swap(&a, &b)
    fmt.Println("a = ", a," b = ", b)

    var p *int
    p = &a
    fmt.Println("&a = ", &a)
    fmt.Println("&b = ", p)

    var pp **int // 二级指针
    pp = &p
    fmt.Println("pp = ", pp)
    fmt.Println("&p = ", &p)
}

运行结果:
image.png

总结

  • 如果希望函数可以修改函数以外的变量的值,可以使用指针
最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏