美文网首页
轻松理解Go函数传参内幕

轻松理解Go函数传参内幕

作者: 皇甫LG | 来源:发表于2020-04-23 23:56 被阅读0次

    一、内置类型作为参数传递

    首先要明确一点:golang语言中是没有引用传递的

    先上结论:
    golang的所有内置类型()作为函数参数传递都是传值的方式,需要注意的是:array、slice和map作为函数参数时也是传值,但是如果对结构内元素进行的修改,修改的是原数据。如果是对其进行整体赋值,则不会修改原数据,相当于拷贝出一个新的临时变量。通过传递指针参数实现修改原数据。

    Go内置基础类型如下

    • 布尔型:bool
    • 整型:int int64 int32 int16 int8 uint8 uint16 uint32 uint64 uint
    • 浮点型:float32 float64
    • 复数型:complex64 complex128
    • 字符串:string
    • 字符型:rune
    • 错误型:error
    • 未命名类型:array、slice、map、channel 等和具体元素类型、长度等有关
    • 接口: interface
    • 函数: funcation

    下面用程序来验证一下:

    package main
    
    import "fmt"
    
    //TypeOfParams 解释函数各个参数到底是值传递还是引用?
    func TypeOfParams(a int, b string, c []int, d [2]int, e map[int]string, f func(int, int), g chan int, h interface{}) {
        fmt.Printf("参数变量地址:a=%p, b=%p, c=%p, d=%p, e=%p, f=%p, g=%p, h=%p\n", &a, &b, &c, &d, &e, &f, &g, &h)
    
    }
    
    func main(){
        a := 10
        b := "abc"
        c := []int{1, 2, 3}
        d := [2]int{10, 20}
        e := make(map[int]string, 2)
        f := func(x, y int) {}
        g := make(chan int, 10)
        var h interface{}
    
        fmt.Printf("变量定义时:a=%p, b=%p, c=%p, d=%p, e=%p, f=%p, g=%p, h=%p\n", &a, &b, &c, &d, &e, &f, &g, &h)
        comm.TypeOfParams(a, b, c, d, e, f, g, h)
    }
    

    执行后:结果显示如下:


    image

    二、什么是值传递

    函数传递的总是原参数的副本、一份拷贝,比如我们传递一个int类型的参数,传递的其实是这个int参数的一个副本。 如果传递的指针类型的参数,传递就是这个指针的一份拷贝,而不是这个指针指向的值。

    看一个关于指针的例子:

    package main
    
    import "fmt"
    
    func PointerParam(ip *int) {
        fmt.Printf("指针为函数参数的内存地址是:%p\n", &ip)
        *ip = 2
    }
    
    func main(){
        p := 1
        ip := &p
        fmt.Printf("原始指针的内存地址是: %p\n", &ip)
        comm.PointerParam(ip)
        fmt.Printf("int值被修改了,新值为: %d\n", p)
    }
    
    # 结果显示
    原始指针的内存地址是: 0xc00000e060
    指针为函数参数的内存地址是:0xc00000e068
    int值被修改了,新值为: 2
    
    

    结论:这个一个指针的拷贝,因为存放这个两个指针的内存地址是不同的,虽然指针指向的值相同,但却是两个不同的指针。

          0xc00000e060
          ------------>     +------------+
                            |   1(int)   |
                            |0xc0000b4048|
                            +------------+
                                  |
                                  |
                                  |
                                  |
          0xc00000e060            |             0xc00000e068
          ------------>     +------------+      <------------
                            |   2(int)   |
                            |0xc0000b4048|
                            +------------+
    

    三、最终结论:

    Go语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝。因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据。

    相关文章

      网友评论

          本文标题:轻松理解Go函数传参内幕

          本文链接:https://www.haomeiwen.com/subject/cbytwhtx.html