一、函数参数传递方式

函数参数传递有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]

最新文章

  1. iPhoneUI元素的大小
  2. 微擎css使用php变量,$_Wamp;全局变量
  3. android中的后退功能,如何在Android应用中实现一个返回键功能
  4. [ZJOI2007]时态同步 树形DP
  5. 接触的第二个引擎 scaleform
  6. iOS开发之Quartz2D 二:绘制直线,曲线,圆弧,矩形,椭圆,圆
  7. MCMC: Metropolis-Hastings, Gibbs and slice sampling
  8. 第二章、音频压缩算法
  9. 如何制作纯净的U盘启动盘
  10. 浏览器是如何渲染网页的
  11. c#开发移动彩信网关
  12. 0xc000007b的解决办法(续)
  13. 11g OCM 考试感悟
  14. RadioButton 只读
  15. 华硕服务器不分区重装系统,华硕笔记本重装系统不能进入系统怎么办
  16. 游戏运动模糊技术讲解
  17. cpu字长、操作系统字长和jvm中各数据类型占用的字节数关系
  18. Android安全性优化——APP加固
  19. ctab法提取dna流程图_CTAB法提取DNA原理及步骤、制胶、电泳
  20. MySQL--SQL执行分析工具

热门文章

  1. java query接口_「软帝学院」Java零基础学习详解
  2. java core日志在哪里_java-如何在未启用日志记录功能的情况下在...
  3. BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [
  4. Java 接口实现计算器加减乘除(字符交互界面)
  5. grep与正则表达式基础
  6. 杜恩德的新博客,都来看看
  7. 立即终止Sleep的线程
  8. dagger2 依赖注入
  9. json-lib解决死循环
  10. 安装mysql 5.5.14 报错