$emit传递多个参数_Go语言参数传递方式

一、函数参数传递方式

函数参数传递有2种方式:值传递和引用传递。

值传递是指调用函数时将参数值复制一份到函数,如果对函数参数进行修改,影响不到实际参数。

引用传递是指调用函数时将参数的指针传递到函数中,函数中对参数的修改,将影响到实际参数。

  不同编程语言的函数参数传递方式不尽相同,例如C语言中的int、float等参数类型采用值传递的方式,而数组类型默认为引用传递,C++中的引用传递显而易见,其类型系统直接包含了引用类型。

二、Go语言中的函数传递方式

开门见山,直接上官方说法“Go语言函数传递方式都是值传递”。int64、string等类型采用值传递比较好理解,但是为什么说slice、interface、map和channel类型也是值传递呢?先看个例子1:

package main import "fmt" func fun0(slice0 []int) {    slice0 = append(slice0, 2)    fmt.Println("func0 : ", slice0)} func main() {    slice0 := []int{1}    fun0(slice0)    fmt.Println("main : ", slice0)}

打印结果:

func0 :  [1 2]main :  [1]

分析:fun0中slice0追加元素并没有影响到main函数中的slice0,可见slice采用的是值传递方式。

我们再看另外一个例子2:

package main import "fmt" func fun0(map0 map[string]int) {    map0["fun0"] = 1    fmt.Println("func0 : ", map0)} func main() {    map0 := map[string]int{"main":1}    fun0(map0)    fmt.Println("main : ", map0)}

打印结果:

func0 :  map[fun0:1 main:1]main :  map[fun0:1 main:1]

分析:func0中map0添加新元素,但是确实是影响到了main函数中的map0,看着好像map采用的是引用传递方式,实则不然。

三、为什么说mapslice等类型采用值传递?

看似slice类型为值传递,map类型为引用传递,而实际上却都是值传递。为什么会出现上述两种不同的情况呢?为了方便说明这种现象,本文根据slice、map、interface和channel等类型的实现做一些抽象,将这些类型统一表示为1个结构体,其中包含一个或多个指针,指向数据真正的存储,如下图所示。

dab788019cb475eacc084abad1fb1f41.png

有了上述抽象,我们再看下,当slice、map、interface和channel变量作为参数传递到另外一个函数时,会出现什么样的情况。以第二节中的2个例子来说明,入参和形参的存储结构如下图所示。

885184f1e14f33a8b9f8b7d766d2bfd4.png 

可以看出,参数传递前后data数据并没有发生变化,但struct结构却是不同的,被调函数中生成了一份struct副本。

3.1 slice的函数传递方式

在例子1中func0中调用append追加了1个元素,有2种可能的情况:1、数组容量不足,append导致数组扩展;2、数组容量充足,append只是在原来的data存储增加了1个元素。两者分别对应的存储如下图所示。

3f61a379ca1ae36a579b9431538f2652.png

第一种情况,struct和data都和原来的入参分道扬镳,fun0中的修改当然不会什么影响到main函数。

109833af907e8d3f3cbd26414ecaee3d.png

第二种情况,slice在参数传递前后共用一段存储data,但是需要注意的是struct结构中的len前后不同,因此main函数中同样不能感知到有新追加的元素。

这就解释了为什么fun0中slice追加新元素,在main函数中无影响。

3.2 Map中的函数传递方式

在例子2中,func0调用map0[“fun0”]=2,调用前后的存储结构,如下图所示。

a16de4131b45d3d2fed2671cb589d904.png

可以看出fun0中追加元素同样在main中也能感知到。那能否据此说,Go语言中的map采用的是引用传递的方式呢?其实是不对的,看个例子3:

package main import "fmt" func fun0(map0 map[string]int) {    map0 = map[string]int {        "fun0":1,    }    fmt.Println("func0 : ", map0)} func main() {    map0 := map[string]int{"main":1}    fun0(map0)    fmt.Println("main : ", map0)}

打印结果:

func0 :  map[fun0:1]main :  map[main:1]

分析:func0中重新赋值map0,并没有影响到main函数,所以map的传递其实也是值传递,从调用前后的存储结构图,也可以一窥究竟。

1a5744bf2a6b6f4ff9def912bb5f1d88.png

  那有没有什么办法不通过返回值重新赋值,使得被调函数的参数改变能够影响到主调函数呢?当然是有办法的,就是直接传递变量的指针例如代码4。不过Go语言并不建议采用这样方式来实现引用传递。

package main import "fmt" func fun0(map0 *map[string]int) {    *map0 = map[string]int {       "fun0":1,    }    fmt.Println("func0 : ", *map0)} func main() {    map0 := map[string]int{"main":1}    fun0(&map0)    fmt.Println("main : ", map0)}

打印结果:

func0 :  map[fun0:1]main :  map[fun0:1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值