轻松理解Go函数传参内幕
2020-04-23 本文已影响0人
皇甫LG
一、内置类型作为参数传递
首先要明确一点: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等这些),这样就可以修改原内容数据。