理解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 总是通过值传递,再加上支持指针的额外表现力,我们就能创建出更清晰的程序。

相关推荐
w***744014 小时前
SpringBoot项目如何导入外部jar包:详细指南
spring boot·后端·jar
tsumikistep16 小时前
【前后端】接口文档与导入
前端·后端·python·硬件架构
码事漫谈16 小时前
为什么C语言拒绝函数重载?非要重载怎么做?
后端
码事漫谈16 小时前
浅谈C++与C语言二进制文件差异(从一次链接错误说起)
后端
空白诗18 小时前
mdcat 在 HarmonyOS 上的构建与适配
后端·安全·华为·rust·harmonyos
y***613119 小时前
SpringBoot集成Flowable
java·spring boot·后端
i***220719 小时前
springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);docker中同时部署应用和libreoffice
spring boot·后端·docker
e***877020 小时前
windows配置永久路由
android·前端·后端
代码or搬砖20 小时前
SpringMVC的执行流程
java·spring boot·后端