理解go指针和值传递

1 简介

在 go社区有句话是: 一切都是值传递, PASS BY VALUE.

当我们传递一个内存地址时,我们传递了一个值

与 C 系列中的所有语言一样,Go 中的所有内容都是通过值传递的。

也就是说,函数总是获取所传递事物的副本,就好像有一个赋值语句将值分配给参数一样。

例如,将 int 值传递给函数会复制 int,传递指针值会复制指针,但不会复制它指向的数据。

本文举例依次说明这几种常见场景。

2 指针的示例

  • 指针引用

该代码使 b 成为指向存储 int 的内存地址的指针,b 的类型为 "int pointer"

arduino 复制代码
    	其中 *int -- * 是类型的一部分 -- b 是 *int 类型
       

代码示例

css 复制代码
	a := 43

  fmt.Println(a)
  fmt.Println(&a)

  var b = *&a

  fmt.Println(b)
  fmt.Println(&b)
  • ** 取消引用**

b 是 int 指针;

b 指向存储 int 的内存地址

要查看该内存地址中的值,请在 b 前面添加一个 * 这称为取消引用,在这种情况下,* 是运算符。

go 复制代码
	a := 43

  fmt.Println(a)   // 43
  fmt.Println(&a)  // 0xc0000180a8
  fmt.Println(*&a) // 43   取消引用

  var b = &a
  fmt.Println(b)   // 0xc0000180a8
  fmt.Println(*b)  // 43
  fmt.Println(&b)  // 0xc000006030
  fmt.Println(*&b) // 0xc0000180a8
 

另一个例子很有用, 我们可以传递一个内存地址而不是一堆值(我们可以传递一个引用) 然后我们仍然可以更改存储在该内存地址中的任何内容的值 这使我们的程序性能更高 我们不必传递大量数据 我们只需要传递地址

go 复制代码
	a := 43 
  fmt.Println(a)  // 43
  fmt.Println(&a) // 0x20818a220

  var b = &a
  fmt.Println(b)  // 0x20818a220
  fmt.Println(*b) // 43

  *b = 42        // b 修改了该地址上的值,改为42  
  fmt.Println(a) // 42
  • 值传递时函数不会改变原参数的值

参数x 传递到 其他函数中 不会改变 参数x的值

go 复制代码
    func zero(z int) {
        z = 0
    }

    func main() {
        x := 5
        zero(x)  // 参数 传递到 其他函数中 不会改变 参数的值
        fmt.Println(x) // x is still 5
    }
  • 值传递时使用指针修改原参数的值

参数x 的地址,被传递到函数中, 函数中取消了引用,并改变 传入参数的值

go 复制代码
func zero(z *int) {
    fmt.Println("from function zero ", z)   //0xc00000a0c8
    *z = 0  //指针加指针 指向了原值
}

func main() {
    x := 5
    fmt.Println(&x)   //0xc00000a0c8
    zero(&x)
    fmt.Println(x) // x 被修改为 0 
}

3 零值汇总

在 Go 语言中,变量定义可以是声明的,也可以通过 make 、 new 函数来声明,

区别在于 make 和 new 函数属于显式声明 display declaration(返回变量实例) 和初始化 initialization(new 返回实例的指针地址)。

如果我们声明的变量未显式初始化,则变量的默认值是该类型的零值。

测试代码

go 复制代码
    var (
        a     [2]int32
        iface interface{}
        it    int32
        ft    float32
        ct    chan int
        st    string
        strct struct{}
        sl    []int32
        mp    map[int]int
        pt    *int
        fct   func()
    )

    fmt.Println("zero of array:", a)
    fmt.Println("zero of interface:", iface)
    fmt.Println("zero of int32:", it)
    fmt.Println("zero of float32:", ft)
    fmt.Println("zero of chan:", ct)
    fmt.Println("zero of string:", st)
    fmt.Println("zero of struct:", strct)
    fmt.Println("zero of slice:", sl)
    fmt.Println("zero of map:", mp)
    fmt.Println("zero of *int:", pt)
    fmt.Println("zero of fuction:", fct)

输出示例:

go 复制代码
zero of array: [0 0]
zero of interface: <nil>
zero of int32: 0
zero of float32: 0
zero of chan: <nil>
zero of string:
zero of struct: {}
zero of slice: []
zero of map: map[]
zero of *int: <nil>
zero of fuction: <nil>

4 小结

如果将变量传递给函数,该函数总是会得到它的副本。

因此,调用方和被调用方有两个具有相同值的自变量。

如果被调用方修改了 parameter variable,则效果对调用方不可见。

认识到 Go 总是通过值传递,再加上支持指针的额外表现力,我们就能创建出更清晰的程序。

相关推荐
草海桐24 分钟前
Go语言中的Context
go·context
一只鹿鹿鹿1 小时前
【测试文档】项目测试文档,测试管理规程,测试计划,测试文档模版,软件测试报告书(Word)
数据库·后端·spring·单元测试
jstart千语1 小时前
【SpringBoot】HttpServletRequest获取使用及失效问题(包含@Async异步执行方案)
java·前端·spring boot·后端·spring
Asthenia04121 小时前
Java线程池任务完成检测的多种方法及面试深度剖析
后端
Goboy2 小时前
SQL面试实战,30分钟征服美女面试官
后端·面试·架构
csdn_HPL2 小时前
SpringBoot + Vue 实现云端图片上传与回显(基于OSS等云存储)
vue.js·spring boot·后端
RainbowSea2 小时前
通用型产品发布解决方案(基于分布式微服务技术栈:SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI
java·spring boot·后端
虽千万人 吾往矣2 小时前
golang channel源码
开发语言·后端·golang