轻松理解Go函数传参内幕

皇甫LG · · 792 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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

首先要明确一点: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等这些),这样就可以修改原内容数据。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:皇甫LG

查看原文:轻松理解Go函数传参内幕

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

792 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传