$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采用的是引用传递方式,实则不然。
三、为什么说map、slice等类型采用值传递?
看似slice类型为值传递,map类型为引用传递,而实际上却都是值传递。为什么会出现上述两种不同的情况呢?为了方便说明这种现象,本文根据slice、map、interface和channel等类型的实现做一些抽象,将这些类型统一表示为1个结构体,其中包含一个或多个指针,指向数据真正的存储,如下图所示。
有了上述抽象,我们再看下,当slice、map、interface和channel变量作为参数传递到另外一个函数时,会出现什么样的情况。以第二节中的2个例子来说明,入参和形参的存储结构如下图所示。
可以看出,参数传递前后data数据并没有发生变化,但struct结构却是不同的,被调函数中生成了一份struct副本。
3.1 slice的函数传递方式
在例子1中func0中调用append追加了1个元素,有2种可能的情况:1、数组容量不足,append导致数组扩展;2、数组容量充足,append只是在原来的data存储增加了1个元素。两者分别对应的存储如下图所示。
第一种情况,struct和data都和原来的入参分道扬镳,fun0中的修改当然不会什么影响到main函数。
第二种情况,slice在参数传递前后共用一段存储data,但是需要注意的是struct结构中的len前后不同,因此main函数中同样不能感知到有新追加的元素。
这就解释了为什么fun0中slice追加新元素,在main函数中无影响。
3.2 Map中的函数传递方式
在例子2中,func0调用map0[“fun0”]=2,调用前后的存储结构,如下图所示。
可以看出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的传递其实也是值传递,从调用前后的存储结构图,也可以一窥究竟。
那有没有什么办法不通过返回值重新赋值,使得被调函数的参数改变能够影响到主调函数呢?当然是有办法的,就是直接传递变量的指针例如代码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]
最新文章
- iPhoneUI元素的大小
- 微擎css使用php变量,$_Wamp;全局变量
- android中的后退功能,如何在Android应用中实现一个返回键功能
- [ZJOI2007]时态同步 树形DP
- 接触的第二个引擎 scaleform
- iOS开发之Quartz2D 二:绘制直线,曲线,圆弧,矩形,椭圆,圆
- MCMC: Metropolis-Hastings, Gibbs and slice sampling
- 第二章、音频压缩算法
- 如何制作纯净的U盘启动盘
- 浏览器是如何渲染网页的
- c#开发移动彩信网关
- 0xc000007b的解决办法(续)
- 11g OCM 考试感悟
- RadioButton 只读
- 华硕服务器不分区重装系统,华硕笔记本重装系统不能进入系统怎么办
- 游戏运动模糊技术讲解
- cpu字长、操作系统字长和jvm中各数据类型占用的字节数关系
- Android安全性优化——APP加固
- ctab法提取dna流程图_CTAB法提取DNA原理及步骤、制胶、电泳
- MySQL--SQL执行分析工具
热门文章
- java query接口_「软帝学院」Java零基础学习详解
- java core日志在哪里_java-如何在未启用日志记录功能的情况下在...
- BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [
- Java 接口实现计算器加减乘除(字符交互界面)
- grep与正则表达式基础
- 杜恩德的新博客,都来看看
- 立即终止Sleep的线程
- dagger2 依赖注入
- json-lib解决死循环
- 安装mysql 5.5.14 报错