美文网首页
Go中都是值传递,切记! 你所了解的引用传递等知识经验从今天开始

Go中都是值传递,切记! 你所了解的引用传递等知识经验从今天开始

作者: stackfuture | 来源:发表于2022-01-15 09:01 被阅读0次

聊聊后端面试那些事

原文

Go中都是值传递-【公粽号:堆栈future】

我看在技术大群里面有人问我,到底值类型,引用类型以及指针类型有什么区别?为什么别人说函数传参是引用传递或者值传递,有的人说是指针传递?

想要彻底搞清楚这个问题,你得先通过表现来理解一下,然后在深入源码理解下你很快就会明白的。

所以网上说的有可能都是错误的,大家千万别被误导!

我们接下来重点解决函数传递参数到底是值传递还是其他类型传递!

三者区别

  • 值类型 就是变量赋值的时候将直接获得一个真实的数据副本,请大家再次看清楚,真实的"数据"副本。比如var int a = 10,b:=a 这就相当于a持有10,b持有a的数据的副本 那就将10拷贝到b指向的内存。

  • 引用类型 就是仅仅是把对象的引用赋给变量,这样就可能导致多个变量引用到一个实际对象实例上。比如a := make([]int, 0),b:=a[2:],那么a和b持有的就是底层对象的引用,说白了a和b就是底层对象的别名。

  • 指针类型 就是赋给变量的是一个内存地址,这个地址指向真实的数据,比如var int a = 10,p:=&a,这个p就是指针变量,它存储的就是a的地址。

注意:在Go中弱化了指针,所以一般只需要关注值和引用类型就可以。

函数传参

首先声明一点:在Go中除了slicemapchannel类型之外的变量都是值类型。

那就好说了,引用类型就三种:slice, map和channel,其他都是值类型。

在Go中引用类型的变量初始化默认是make,但是new也是可以的,比如:

<pre data-tool="mdnice编辑器" style="box-sizing: border-box !important; margin: 10px auto 1rem; padding: 0px; outline: 0px; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12.25px; overflow: auto; display: block; color: rgb(33, 37, 41); max-width: 100%; overflow-wrap: break-word !important; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px;">`package main

import "fmt"

type A []int

func main() {
a := new(A)
a = append(a, 1)
fmt.Println(*a) //[1]

a1 := make([]int, 0)
a1 = append(a1, 1)
fmt.Println(a1) //[1]
}` </pre>

当然map以及channel也是可以用makenew的,大家下去试试哈。

至于makenew的区别大家下去自己看哈,这不是本文的重点。

接下来我们重点来聊聊函数传参的问题,我们都知道值传递是copy一份,所以这个大家都能理解,我们重点讲解大家疑惑的问题,引用类型的传参问题。

首先大家看一个例子,即看看它的表现是什么:

<pre data-tool="mdnice编辑器" style="box-sizing: border-box !important; margin: 10px auto 1rem; padding: 0px; outline: 0px; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12.25px; overflow: auto; display: block; color: rgb(33, 37, 41); max-width: 100%; overflow-wrap: break-word !important; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px;">`package main

import "fmt"

func CopyList(a []int) {
a = append(a, 2)
fmt.Printf("copylist is %p \n", &a)
fmt.Println("copylist value is ", a)
}
func main() {
a := make([]int, 0)
a = append(a, 1)
fmt.Println("main value is ", a)
fmt.Printf("main address is %p \n", &a)
CopyList(a)
}

输出:
main value is [1]
main address is 0xc0000a6018

copylist is 0xc0000a6048
copylist value is [1 2]` </pre>

我们通过结果分析下引用传递会有什么影响。

  1. 首先函数参数是a []int
  2. main中定义的引用类型变量是a并且也做了初始化
  3. 然后调用CopyList(a)
  4. 看在main中a的地址和在CopyList中a的地址,发现不一样?为什么不一样呢?因为Go语言在设计的时候明确表示函数参数传递是值传递,所以相当于函数CopyList拷贝了一份变量,这个时候次a非比a。但是那你可能说:为什么拷贝了一份,但是从结果来看就是引用啊,因为函数CopyList改变了外面main中a的值。

答案就是Go在设计引用类型的时候,比如这三者map,slice和channel,他们的结构体中并没有直接保存数据,而是保存了指向数据的指针,那么在函数中传递这三种的变量其实只是拷贝了一份它们自己的一份结构体,但是结构体里面具体保存的指针指向的地址还是同一份,没有变化的。所以这也就说明函数中为什么可以改变引用类型变量的值了,因为底层指针指向的地址是同一个。

分析完了slice,其它两个都比较简单,这里就不再演示了,大家下去自己尝试。

图片

<pre data-tool="mdnice编辑器" style="box-sizing: border-box !important; margin: 10px auto 1rem; padding: 0px; outline: 0px; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12.25px; overflow: auto; display: block; color: rgb(33, 37, 41); max-width: 100%; overflow-wrap: break-word !important; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px;">type slice struct { array unsafe.Pointer //指针 到时候slice初始化的时候会用mallocgc分配内存,把这块内存地址保存到array中。 len int cap int } </pre>

map和channel里面也是有指针,指向一块内存空间。

小结

大家一定要记住在Go中函数传递参数一定是值传递,千万别搞混淆了,至于为什么上面也说清楚了,如果大家还不懂就进群,大牛比较多为你继续解惑哈。

相关文章

  • Go中都是值传递,切记! 你所了解的引用传递等知识经验从今天开始

    聊聊后端面试那些事 原文[https://mp.weixin.qq.com/s?__biz=MzUzMTUxMzY...

  • Go语言学习笔记(三)

    指针 Go的指针不能运算 参数传递 Go的参数传递是值传递(值传递:开辟新的空间,拷贝传递参数的值,引用传递:引用...

  • 面试题(一)

    1. Go 语言参数传递是值传递还是引用传递 Go 语言中所有的传参都是值传递,都是一个副本,一个拷贝。因为拷贝的...

  • golang笔记之函数

    函数参数传递类型 1.按值传递:Go 默认使用按值传递来传递参数,也就是传递参数的副本。 2.引用传递:如果你希望...

  • golang 笔记

    1、go语言只有值传递,没有引用传递。但可以用指针方式实现引用传递 定义:

  • 快速区别值传递和引用传递

    复习Java知识的时候又看到值传递和引用传递的区别,然后就查询一下,结果都是Java到底是值传递还是引用传递之争,...

  • 聊聊Java内部类

    一.磨叽磨叽Java值传递与引用传递 “在Java里面参数传递都是按值传递”即:按值传递是传递的值的拷贝,按引用传...

  • java--参数传递问题

    【前方注意】:java中只存在值传递,值传递,值传递!!! 一、值传递(一切传引用其实本质上是传值) 总所周知,j...

  • Java值传递

    “在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值...

  • golang学习笔记9:函数参数与返回值

    按值传递和按引用传递 Go 默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过...

网友评论

      本文标题:Go中都是值传递,切记! 你所了解的引用传递等知识经验从今天开始

      本文链接:https://www.haomeiwen.com/subject/wdlthrtx.html