Go 参数传递

在面试中,经常会被问起,这门语言的参数传递是值传递还是引用传递,当然,大部分情况下我们都会提前准备,有恃无恐,但还是希望能够精益求精嘛,所以针对Go语言来分析,Go传参是值传递还是引用传递。那首先,我们先来了解什么是值传递,什么是引用传递。

值传递

值传递就是函数传递的是传进来参数的一个副本。换个说法就是函数内部修改传入参数的值是函数外部传入值得一个拷贝,所以你在函数内部对这个值进行修改也不会影响外部该参数的值,的确很难表达,看个案例:

package main

import "fmt"


func modify(a int){
    a = 10
}

func main(){
    a := 20
    fmt.Println(a)
    modify(a)
    fmt.Println(a)
}

输出:

20
20

很直观,参数a在进入函数后并没有被改变,我们还可以看看两个内存地址是否相同

package main

import "fmt"


func modify(a int){
    fmt.Println(&a)
    a = 10
}

func main(){
    a := 20
    fmt.Println(&a)
    modify(a)
}

输出为:

0xc000052080
0xc000052088

可以看到两个内存地址并不一样,这就对了,可以说明modify中的参数a,并不是主函数中的变量a,而是a的一个拷贝而已,所以在函数中修改的也是拷贝a的值,对函数外的这个a并没有改动。

这个时候,我们就会使用指针来修改值,指针案例:

func main() {
        i:=10
        ip:=&i
        fmt.Printf("原始指针的内存地址是:%p\n",&ip)
        modify(ip)
        fmt.Println("int值被修改了,新值为:",i)
}

 func modify(ip *int){
        fmt.Printf("函数里接收到的指针的内存地址是:%p\n",&ip)
        *ip=1
 }

输出:

原始指针的内存地址是:0xc000006028
函数里接收到的指针的内存地址是:0xc000006038
int值被修改了,新值为: 1

可以看到两次内存地址也是不一样的,所以其实指针也是值传递,但是指针保存的是内存地址,而在函数中修改语句"*ip=1",其实是在修改变量i内存地址对应的值,那自然就是能够修改的。

引用传递

Go语言其实并没有引用传递,上面的例子可以证明如果内存地址不同自然就是值传递,那内存地址相同就是引用传递。

我们再通过Map类型进行验证,Map类型可以通过函数修改内容,但是他没有明显的指针。

func modify(p map[string]int){
        fmt.Printf("函数里接收到map的内存地址是:%p\n",&p)
        p["张三"] = 20 
}

func main(){
    person := make(map[string]int)
    person["张三"] = 56
    fmt.Printf("原始map的内存地址是:%p\n",mp)
        modify(person)
        fmt.Println("map值被修改了,新值为:",person)
}

输出:

原始map的内存地址是:0xc00007e018
函数里接收到map的内存地址是:0xc00007e028
map值被修改了,新值为: map[张三:20]

发现两个内存地址是不一样的,所以这又是一个值传递,那为什么没有使用指针却修改了Map的内容?

查看一下map源码

func makemap(t *rtype, cap int) (m unsafe.Pointer)

我们发现这个方法返回的是m unsafe.Pointer继续查看一下unsafe.Pointer类型是什么?

type Pointer *ArbitraryType

返回的还是一个指针,所以我们可以得出map的传递也是值传递。

结论

确认Go语言中所有的传参都是值传递。但是传递的类型如果是int、string、struct等这些,那在函数中无法修改原参数内容数据;如果是指针、map、slice、chan等这些,在函数中可以修改原参数内容数据。