原创

Go的参数值传递与引用传递

接下来我们讲一下Go中的参数传递原理。

关于参数传递是一个什么概念呢,参数传递相关的知识是在研究当调用一个函数时,把外部的一个变量传入函数内,在函数内修改这个参数是否会对外部的参数变量的值有影响。参数传递用在的一个地方是函数的参数传递。(还有方法的接收器参数传递)

比如李明今天没有写作业,到了学校后匆匆忙忙的找小红要作业本(小红的作业本为方法调用处传入的参数),想要抄一抄补上,所以李明有一个抄作业的任务(抄作业的任务为函数),那么他有两个选择可以完成抄作业的任务。

第一个是直接拿过来小红的作业本开始抄,这在函数中叫做引用传递,因为如果小明抄的时候不小心桌子上的水打翻了,弄湿了小红的作业本,小红的作业本就真湿了,没法交了。

第二个是用打印机把小红的作业打印一份,然后拿着打印的那份抄,这叫做值传递,也就是说我拷贝一份值来用,那么我在抄作业(任务函数内)无论怎么弄湿小红的作业本,小红真正的自己的作业本也不受到影响。

在编程语言的函数中,如果是值传递,则是一个拷贝,在方法内部修改该参数值无法对其本身造成影响,如果是引用传递的概念,则可以改变其对象本身的值。
在Go语言中只有值传递,也是是说,无论如何Go的参数传递的都是一个拷贝。

重点来了:
Go中的值传递有两种类型,如下:

1.第一种值传递是具体的类型对象值传递,可能是int,string,struct之类的。
在此时,如果我们要自定义一个struct类型,传入参数中,可能遇到一个坑,因为是值传递,所以会拷贝一个struct对象,如果这个对象占内存比较大,而且这个函数调用频繁,会大量的拷贝消耗性能资源。

2.第二种传递是叫指针参数类型的值传递,此时参数是一个指针类型,到具体的方法中,我们的参数也要用指针类型的参数接受,但是此时Go语言的内部做了一个黑箱操作。
举例(下面还有完整可执行代码示例,先文字和伪代码举例):
我们有一个类型为Boy的结构体,还有一个方法Mod

func Mod(b *Boy){

}

这个Mod方法的参数是一个指针类型的Boy对象,
我们要调用的时候应该这样传参数:

var boy = Boy{} 
//用&取boy对象的指针地址,然后传入Mod方法
Mod(&boy)

我们看看下面的代码示例:

package main
import "fmt"
// Boy 结构体
type Boy struct {
   name string
   age int
}

func Mod(b *Boy) {
   //这个是获取调用方法传入的参数的地址值
   fmt.Printf("b的值(之前boy的地址)是%p\n",b)
   //这个是获取本函数中 b这个指针变量的地址
   fmt.Printf("b这个指针自己的地址是=%p\n",&b)
   //打印值
   //这里自动转换使指针可以直接点出来属性
   fmt.Println(b.name,b.age)
}

func main() {
    boy := Boy{"li_ming",20}
    fmt.Printf("main函数中的boy地址是:%p\n",&boy)
    //将boy的地址 放入Mod函数的参数中
    Mod(&boy)
    //注意!!!下面有黑箱操作:
    /* //在&boy并放入Mod传递的过程中实际上做了如下黑箱操作
    b := new(Boy)   //创建一个名为b的类型为Boy的指针变量
    b = &boy      //把boy的地址存入b这个指针变量内
    //接着把b放入func Mod(b *Boy)的参数中,然后,开始执行Mod方法。
    fmt.Println(b.name,b.age)
    fmt.Printf("b的地址是:%p\n",&b)
    fmt.Printf("b的值是:%p\n",b)

    //输出结果
    //main函数中的boy地址是:0x10aec0c0
    //li_ming 20
    //b的地址是:0x10ae40f8
    //b的值是:0x10aec0c0
     */

    /*//以下代码无用,是指为了加深理解new,可以试试输出结果
    boy2 := new(Boy)
    fmt.Printf("main函数中new的boy2地址是:%p\n",boy2)
    boy2.name = "xiaohong"
    boy2.age = 18
    Mod(boy2)
     */
}

所以,Go中的参数传递所有的都是值传递,只不过值传递中,值可以是指针类型,是创建了一个新的指针存储原来参数(这个参数是原对象的地址)的值。
所以你用原对象的地址改它的属性,是有点类似于引用类型传递的效果的。
为啥说指针类型也是值传递,因为他还是创建了一个新的指针对象,值传递就是拷贝,拷贝就得创建对象,只不过这个新的指针变量存储的值是原来的参数对象的地址。

最后总结一下:
1.Go的参数传递都是值传递。
2.指针类型的值传递可以改变原来对象的值。
3.make和new从底层原理上创建的所有对象都是指针对象,所以make和new创建出来的slice,map,chan或者其它任何对象都是指针传递,改变值后都可以使原来的对象属性发生变化。

本文链接地址:http://www.ysxbohui.com/article/195

正文到此结束